# frozen_string_literal: true RSpec.describe DiscourseAi::Admin::AiEmbeddingsController do fab!(:admin) before { sign_in(admin) } let(:valid_attrs) do { display_name: "Embedding config test", dimensions: 1001, max_sequence_length: 234, pg_function: "<#>", provider: "hugging_face", url: "https://test.com/api/v1/embeddings", api_key: "test", tokenizer_class: "DiscourseAi::Tokenizer::BgeM3Tokenizer", embed_prompt: "I come first:", search_prompt: "prefix for search", matryoshka_dimensions: true, } end describe "POST #create" do context "with valid attrs" do it "creates a new embedding definition" do post "/admin/plugins/discourse-ai/ai-embeddings.json", params: { ai_embedding: valid_attrs } created_def = EmbeddingDefinition.last expect(response.status).to eq(201) expect(created_def.display_name).to eq(valid_attrs[:display_name]) expect(created_def.embed_prompt).to eq(valid_attrs[:embed_prompt]) expect(created_def.search_prompt).to eq(valid_attrs[:search_prompt]) expect(created_def.matryoshka_dimensions).to eq(true) end it "stores provider-specific config params" do post "/admin/plugins/discourse-ai/ai-embeddings.json", params: { ai_embedding: valid_attrs.merge( provider: "open_ai", provider_params: { model_name: "embeddings-v1", }, ), } created_def = EmbeddingDefinition.last expect(response.status).to eq(201) expect(created_def.provider_params["model_name"]).to eq("embeddings-v1") end it "ignores parameters not associated with that provider" do post "/admin/plugins/discourse-ai/ai-embeddings.json", params: { ai_embedding: valid_attrs.merge(provider_params: { custom: "custom" }), } created_def = EmbeddingDefinition.last expect(response.status).to eq(201) expect(created_def.lookup_custom_param("custom")).to be_nil end end context "with invalid attrs" do it "doesn't create a new embedding defitinion" do post "/admin/plugins/discourse-ai/ai-embeddings.json", params: { ai_embedding: valid_attrs.except(:provider), } created_def = EmbeddingDefinition.last expect(created_def).to be_nil end end end describe "PUT #update" do fab!(:embedding_definition) context "with valid update params" do let(:update_attrs) { { provider: "open_ai" } } it "updates the model" do put "/admin/plugins/discourse-ai/ai-embeddings/#{embedding_definition.id}.json", params: { ai_embedding: update_attrs, } expect(response.status).to eq(200) expect(embedding_definition.reload.provider).to eq(update_attrs[:provider]) end it "returns a 404 if there is no model with the given Id" do put "/admin/plugins/discourse-ai/ai-embeddings/9999999.json" expect(response.status).to eq(404) end it "doesn't allow dimenstions to be updated" do new_dimensions = 200 put "/admin/plugins/discourse-ai/ai-embeddings/#{embedding_definition.id}.json", params: { ai_embedding: { dimensions: new_dimensions, }, } expect(response.status).to eq(200) expect(embedding_definition.reload.dimensions).not_to eq(new_dimensions) end end context "with invalid update params" do it "doesn't update the model" do put "/admin/plugins/discourse-ai/ai-embeddings/#{embedding_definition.id}.json", params: { ai_embedding: { url: "", }, } expect(response.status).to eq(422) end end end describe "DELETE #destroy" do fab!(:embedding_definition) it "destroys the embedding defitinion" do expect { delete "/admin/plugins/discourse-ai/ai-embeddings/#{embedding_definition.id}.json" expect(response).to have_http_status(:no_content) }.to change(EmbeddingDefinition, :count).by(-1) end it "validates the model is not in use" do SiteSetting.ai_embeddings_selected_model = embedding_definition.id delete "/admin/plugins/discourse-ai/ai-embeddings/#{embedding_definition.id}.json" expect(response.status).to eq(409) expect(embedding_definition.reload).to eq(embedding_definition) end end describe "GET #test" do context "when we can generate an embedding" do it "returns a success true flag" do WebMock.stub_request(:post, valid_attrs[:url]).to_return(status: 200, body: [[1]].to_json) get "/admin/plugins/discourse-ai/ai-embeddings/test.json", params: { ai_embedding: valid_attrs, } expect(response).to be_successful expect(response.parsed_body["success"]).to eq(true) end end context "when we cannot generate an embedding" do it "returns a success false flag and the error message" do error_message = { error: "Embedding generation failed." } WebMock.stub_request(:post, valid_attrs[:url]).to_return( status: 422, body: error_message.to_json, ) get "/admin/plugins/discourse-ai/ai-embeddings/test.json", params: { ai_embedding: valid_attrs, } expect(response).to be_successful expect(response.parsed_body["success"]).to eq(false) expect(response.parsed_body["error"]).to eq(error_message.to_json) end end end end