Skip to content

Commit 652b43b

Browse files
authored
[Search] Unskip Tests & Update Tests Recordings (Azure#37213)
### Packages impacted by this PR @azure/search-documents ### Describe the problem that is addressed by this PR Update the test suite & recordings. Live pipeline is passing [here](https://dev.azure.com/azure-sdk/internal/_build/results?buildId=5838855&view=results). The changes include: - Creating a separate folder for preview only feature. Any tests outside this folder should be passing for both stable & preview version. - Unskip all the tests that are common for both stable and preview version. Add recordings for those tests. - Remove usage of `models` property and AOAI client dependency. Update `KnowledgeRetrievalClient.retrieveKnowledge` test to use `intents` with `minimal` reasoning effort to remove `models` property **Known Issues**: - The only unskipped test suite is the `KnowledgeRetrievalClient` with AOAI models. We don't have the resource to run this in the live pipeline, but it can be unskipped to run locally before release. - `models` is not a required property in the service for `KnowledgeBase` and JS marks this as required. This is a known issue that will be fixed for the next preview release. Currently using `as any` to get around this. ### What are the possible designs available to address the problem? If there are more than one possible design, why was the one in this PR chosen? ### Are there test cases added in this PR? _(If not, why?)_ ### Provide a list of related PRs _(if any)_ ### Command used to generate this PR:**_(Applicable only to SDK release request PRs)_ ### Checklists - [ ] Added impacted package name to the issue description - [ ] Does this PR needs any fixes in the SDK Generator?** _(If so, create an Issue in the [Autorest/typescript](https://github.com/Azure/autorest.typescript) repository and link it here)_ - [ ] Added a changelog (if necessary)
1 parent e34092f commit 652b43b

File tree

8 files changed

+3244
-337
lines changed

8 files changed

+3244
-337
lines changed

sdk/search/search-documents/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "js",
44
"TagPrefix": "js/search/search-documents",
5-
"Tag": "js/search/search-documents_37ea47f7d6"
5+
"Tag": "js/search/search-documents_4c94109848"
66
}

sdk/search/search-documents/test/public/node/knowledgeRetrievalClient.spec.ts renamed to sdk/search/search-documents/test/public/node/preview/knowledgeRetrievalClient.spec.ts

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,32 @@
22
// Licensed under the MIT License.
33

44
import { createTestCredential } from "@azure-tools/test-credential";
5-
import { Recorder } from "@azure-tools/test-recorder";
5+
import { env, Recorder } from "@azure-tools/test-recorder";
66
import { delay } from "@azure/core-util";
7-
import type { OpenAIClient } from "@azure/openai";
8-
import { afterEach, assert, beforeEach, describe, it } from "vitest";
97
import type {
10-
AzureOpenAIParameters,
11-
RemoteSharePointKnowledgeSource,
128
SearchClient,
139
SearchIndexClient,
1410
WebKnowledgeSource,
15-
} from "../../../src/index.js";
16-
import { KnowledgeRetrievalClient, KnownKnowledgeRetrievalOutputMode } from "../../../src/index.js";
17-
import { defaultServiceVersion } from "../../../src/serviceUtils.js";
18-
import type { Hotel } from "../utils/interfaces.js";
19-
import { createClients } from "../utils/recordedClient.js";
20-
import { createIndex, createRandomIndexName, populateIndex, WAIT_TIME } from "../utils/setup.js";
21-
22-
// TODO: Remove skip and fix recording issues before enabling these tests in PRs
23-
// To run these tests locally in 'live' mode, remove the skip modifier
24-
describe.skip("KnowledgeRetrievalClient", { timeout: 20_000 }, () => {
11+
RemoteSharePointKnowledgeSource,
12+
} from "@azure/search-documents";
13+
import {
14+
KnowledgeRetrievalClient,
15+
KnownKnowledgeRetrievalOutputMode,
16+
} from "@azure/search-documents";
17+
import { defaultServiceVersion } from "../../../../src/serviceUtils.js";
18+
import { afterEach, assert, beforeEach, describe, it } from "vitest";
19+
import type { Hotel } from "../../utils/interfaces.js";
20+
import { createClients } from "../../utils/recordedClient.js";
21+
import { createRandomIndexName, createIndex, WAIT_TIME, populateIndex } from "../../utils/setup.js";
22+
23+
describe("Knowledge", { timeout: 20_000 }, () => {
2524
let recorder: Recorder;
2625
let searchClient: SearchClient<Hotel>;
2726
let indexClient: SearchIndexClient;
28-
let openAIClient: OpenAIClient;
2927
let TEST_INDEX_NAME: string;
3028
let TEST_BASE_NAME: string;
29+
let TEST_KS_NAME: string;
3130
let knowledgeRetrievalClient: KnowledgeRetrievalClient;
32-
let chatAzureOpenAIParameters: AzureOpenAIParameters;
3331

3432
beforeEach(async (ctx) => {
3533
recorder = new Recorder(ctx);
@@ -38,22 +36,21 @@ describe.skip("KnowledgeRetrievalClient", { timeout: 20_000 }, () => {
3836
({
3937
searchClient,
4038
indexClient,
41-
openAIClient,
4239
knowledgeRetrievalClient,
4340
indexName: TEST_INDEX_NAME,
4441
baseName: TEST_BASE_NAME,
45-
chatAzureOpenAIParameters,
4642
} = await createClients<Hotel>(
4743
defaultServiceVersion,
4844
recorder,
4945
TEST_INDEX_NAME,
5046
TEST_BASE_NAME,
5147
));
48+
TEST_KS_NAME = `searchindex-ks-${TEST_INDEX_NAME}`;
5249
await createIndex(indexClient, TEST_INDEX_NAME, defaultServiceVersion);
5350

5451
await indexClient.createKnowledgeSource({
5552
kind: "searchIndex",
56-
name: "searchindex-ks",
53+
name: TEST_KS_NAME,
5754
searchIndexParameters: {
5855
searchIndexName: TEST_INDEX_NAME,
5956
searchFields: [{ name: "hotelName" }, { name: "description" }],
@@ -63,22 +60,16 @@ describe.skip("KnowledgeRetrievalClient", { timeout: 20_000 }, () => {
6360

6461
await indexClient.createKnowledgeBase({
6562
name: TEST_BASE_NAME,
66-
models: [
67-
{
68-
kind: "azureOpenAI",
69-
azureOpenAIParameters: chatAzureOpenAIParameters,
70-
},
71-
],
72-
knowledgeSources: [{ name: "searchindex-ks" }],
73-
});
63+
knowledgeSources: [{ name: TEST_KS_NAME }],
64+
} as any);
7465

7566
await delay(WAIT_TIME);
76-
await populateIndex(searchClient, openAIClient);
67+
await populateIndex(searchClient);
7768
});
7869

7970
afterEach(async () => {
8071
await indexClient.deleteKnowledgeBase(TEST_BASE_NAME);
81-
await indexClient.deleteKnowledgeSource("searchindex-ks");
72+
await indexClient.deleteKnowledgeSource(TEST_KS_NAME);
8273
await indexClient.deleteIndex(TEST_INDEX_NAME);
8374
await delay(WAIT_TIME);
8475
await recorder?.stop();
@@ -87,19 +78,29 @@ describe.skip("KnowledgeRetrievalClient", { timeout: 20_000 }, () => {
8778
describe("KnowledgeRetrievalClient", () => {
8879
it("executes queries", { timeout: 60000 }, async () => {
8980
const result = await knowledgeRetrievalClient.retrieveKnowledge({
90-
messages: [
81+
intents: [
9182
{
92-
role: "user",
93-
content: [{ type: "text", text: "What is the most luxurious hotel?" }],
83+
type: "semantic",
84+
search: "What is the most luxurious hotel?",
9485
},
9586
],
87+
retrievalReasoningEffort: { kind: "minimal" },
9688
});
9789

9890
assert.exists(result.activity);
9991
assert.exists(result.references);
10092
assert.exists(result.response);
10193
assert.isTrue(result.response.length > 0);
10294
});
95+
});
96+
97+
// Unskip and test locally with valid AOAI resource. Cannot enable in the pipeline due to resource constraints.
98+
describe.skip("KnowledgeRetrievalClient with models", () => {
99+
const chatAzureOpenAIParameters = {
100+
deploymentId: env.AZURE_OPENAI_CHAT_DEPLOYMENT_NAME,
101+
resourceUrl: env.AZURE_OPENAI_ENDPOINT,
102+
modelName: "gpt-4o",
103+
};
103104

104105
it("create webKnowledgeSource and query knowledge base", { timeout: 60000 }, async () => {
105106
const webKnowledgeSource: WebKnowledgeSource = {
@@ -149,10 +150,11 @@ describe.skip("KnowledgeRetrievalClient", { timeout: 20_000 }, () => {
149150
await indexClient.deleteKnowledgeSource("web-ks");
150151
});
151152

152-
it("CRUD remote sharepoint knowledge source", { timeout: 60000 }, async () => {
153+
it("CRUD remote sharepoint knowledge source", { timeout: 60000 }, async () => {
154+
const spKsName = `remotesharepoint-ks-${TEST_INDEX_NAME}`;
153155
const remoteSharePointKnowledgeSource: RemoteSharePointKnowledgeSource = {
154156
kind: "remoteSharePoint",
155-
name: "remotesharepoint-ks",
157+
name: spKsName,
156158
remoteSharePointParameters: {},
157159
};
158160

@@ -169,13 +171,13 @@ describe.skip("KnowledgeRetrievalClient", { timeout: 20_000 }, () => {
169171
azureOpenAIParameters: chatAzureOpenAIParameters,
170172
},
171173
],
172-
knowledgeSources: [{ name: "remotesharepoint-ks" }],
174+
knowledgeSources: [{ name: spKsName }],
173175
outputMode: KnownKnowledgeRetrievalOutputMode.AnswerSynthesis,
174176
});
175177

176178
await delay(WAIT_TIME);
177179
await indexClient.deleteKnowledgeBase(`${TEST_BASE_NAME}-remotesharepoint`);
178-
await indexClient.deleteKnowledgeSource("remotesharepoint-ks");
180+
await indexClient.deleteKnowledgeSource(spKsName);
179181
});
180182
});
181183
});
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import { Recorder } from "@azure-tools/test-recorder";
5+
import { createTestCredential } from "@azure-tools/test-credential";
6+
import { delay } from "@azure/core-util";
7+
import { afterEach, assert, beforeEach, describe, it } from "vitest";
8+
import type { SearchIndex, SearchIndexClient } from "../../../../src/index.js";
9+
import { KnownQueryLanguage, KnownQuerySpeller, SearchClient } from "../../../../src/index.js";
10+
import { defaultServiceVersion } from "../../../../src/serviceUtils.js";
11+
import type { Hotel } from "../../utils/interfaces.js";
12+
import { createClients } from "../../utils/recordedClient.js";
13+
import { createIndex, createRandomIndexName, populateIndex, WAIT_TIME } from "../../utils/setup.js";
14+
15+
describe("search scenarios", { timeout: 20_000 }, () => {
16+
let recorder: Recorder;
17+
let searchClient: SearchClient<Hotel>;
18+
let indexClient: SearchIndexClient;
19+
let TEST_INDEX_NAME: string;
20+
let TEST_BASE_NAME: string;
21+
let indexDefinition: SearchIndex;
22+
23+
beforeEach(async (ctx) => {
24+
recorder = new Recorder(ctx);
25+
TEST_INDEX_NAME = createRandomIndexName();
26+
TEST_BASE_NAME = createRandomIndexName();
27+
({
28+
searchClient,
29+
indexClient,
30+
indexName: TEST_INDEX_NAME,
31+
baseName: TEST_BASE_NAME,
32+
} = await createClients<Hotel>(
33+
defaultServiceVersion,
34+
recorder,
35+
TEST_INDEX_NAME,
36+
TEST_BASE_NAME,
37+
));
38+
indexDefinition = await createIndex(indexClient, TEST_INDEX_NAME, defaultServiceVersion);
39+
await delay(WAIT_TIME);
40+
await populateIndex(searchClient);
41+
});
42+
43+
afterEach(async () => {
44+
await indexClient.deleteIndex(TEST_INDEX_NAME);
45+
await delay(WAIT_TIME);
46+
await recorder?.stop();
47+
});
48+
49+
const baseSemanticOptions = () =>
50+
({
51+
queryLanguage: KnownQueryLanguage.EnUs,
52+
queryType: "semantic",
53+
semanticSearchOptions: {
54+
configurationName:
55+
indexDefinition.semanticSearch?.configurations?.[0].name ??
56+
assert.fail("No semantic configuration in index."),
57+
},
58+
}) as const;
59+
60+
it("search with speller (preview)", async () => {
61+
const searchResults = await searchClient.search("budjet", {
62+
skip: 0,
63+
top: 5,
64+
includeTotalCount: true,
65+
queryLanguage: KnownQueryLanguage.EnUs,
66+
speller: KnownQuerySpeller.Lexicon,
67+
});
68+
assert.equal(searchResults.count, 6);
69+
});
70+
71+
it("search with document debug info", async () => {
72+
const baseOptions = baseSemanticOptions();
73+
const options = {
74+
...baseOptions,
75+
semanticSearchOptions: {
76+
...baseOptions.semanticSearchOptions,
77+
errorMode: "fail",
78+
debugMode: "semantic",
79+
},
80+
} as const;
81+
const searchResults = await searchClient.search("luxury", options);
82+
for await (const result of searchResults.results) {
83+
assert.deepEqual(
84+
{
85+
contentFields: [
86+
{
87+
name: "description",
88+
state: "used",
89+
},
90+
],
91+
keywordFields: [
92+
{
93+
name: "tags",
94+
state: "used",
95+
},
96+
],
97+
rerankerInput: {
98+
content:
99+
"Best hotel in town if you like luxury hotels. They have an amazing infinity pool, a spa, and a really helpful concierge. The location is perfect -- right downtown, close to all the tourist attractions. We highly recommend this hotel.",
100+
keywords: "pool\r\nview\r\nwifi\r\nconcierge",
101+
title: "Fancy Stay",
102+
},
103+
titleField: {
104+
name: "hotelName",
105+
state: "used",
106+
},
107+
},
108+
result.documentDebugInfo?.semantic,
109+
);
110+
}
111+
});
112+
});
113+
114+
describe("content security (preview)", { timeout: 20_000 }, () => {
115+
let recorder: Recorder;
116+
let indexClient: SearchIndexClient;
117+
let index: SearchIndex;
118+
119+
beforeEach(async (ctx) => {
120+
recorder = new Recorder(ctx);
121+
({ indexClient } = await createClients<Hotel>(defaultServiceVersion, recorder, "", ""));
122+
index = {
123+
name: "content-security-test",
124+
purviewEnabled: true,
125+
fields: [
126+
{
127+
type: "Edm.String",
128+
name: "id",
129+
key: true,
130+
},
131+
{
132+
name: "sensitivityLabel",
133+
type: "Edm.String",
134+
filterable: false,
135+
sortable: false,
136+
facetable: true,
137+
sensitivityLabel: true,
138+
},
139+
],
140+
};
141+
await indexClient.createOrUpdateIndex(index);
142+
await delay(WAIT_TIME);
143+
});
144+
145+
afterEach(async () => {
146+
await indexClient.deleteIndex(index.name);
147+
await recorder?.stop();
148+
});
149+
150+
it("verify content security indexes", async () => {
151+
const documents = [
152+
{ id: "1", sensitivityLabel: "87867195-f2b8-4ac2-b0b6-6bb73cb33afc" },
153+
{ id: "2", sensitivityLabel: "9fbde396-1a24-4c79-8edf-9254a0f35055" },
154+
{ id: "3", sensitivityLabel: "1a19d03a-48bc-4359-8038-5b5f6d5847c3" },
155+
{ id: "4", sensitivityLabel: "1a19d03a-48bc-4359-0000-5b5f6d5847c4" },
156+
];
157+
158+
const searchClient = new SearchClient<{ id: string; sensitivityLabel: string }>(
159+
indexClient.endpoint,
160+
index.name,
161+
createTestCredential(),
162+
recorder.configureClientOptions({}),
163+
);
164+
165+
await searchClient.uploadDocuments(documents);
166+
await delay(WAIT_TIME);
167+
168+
// Test that search with invalid authorization token throws an error
169+
let errorThrown = false;
170+
try {
171+
await searchClient.search("*", {
172+
xMsQuerySourceAuthorization: "Invalid token",
173+
xMsEnableElevatedRead: true,
174+
});
175+
} catch (ex: any) {
176+
errorThrown = true;
177+
// Verify it's an auth related error
178+
assert.isTrue(ex.message.includes("Invalid header"));
179+
}
180+
assert.isTrue(errorThrown, "Expected search with invalid header to throw an error");
181+
});
182+
});

0 commit comments

Comments
 (0)