Fix playwright tests (#2325)

* Fix cleanupDBs.js: use async iteration for Azure SDK paged results

* update node version

* fix cleanup script to use new Cosmos SDK APIs correctly

* get rid of global crypto

* fix flakiness

* revert DE pipeline to use Node 18

* nit

---------

Co-authored-by: Asier Isayas <aisayas@microsoft.com>
This commit is contained in:
asier-isayas
2026-04-03 14:34:10 -04:00
committed by GitHub
parent 2ba58cd1a5
commit aa70cf994a
3 changed files with 39 additions and 31 deletions

View File

@@ -22,18 +22,18 @@ jobs:
env:
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: "Az CLI login"
uses: azure/login@v1
uses: azure/login@v2
with:
client-id: ${{ secrets.E2E_TESTS_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Use Node.js 18.x
uses: actions/setup-node@v1
- name: Use Node.js 20.x
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
- run: npm ci
- run: node utils/cleanupDBs.js

View File

@@ -24,6 +24,14 @@ test.describe("Vector Policy under Scale & Settings", () => {
await vectorPolicyTab.click();
});
test.afterEach("Clear vector policies", async () => {
const { resource: containerDef } = await context.container.read();
if (containerDef.vectorEmbeddingPolicy?.vectorEmbeddings?.length) {
containerDef.vectorEmbeddingPolicy.vectorEmbeddings = [];
await context.container.replace(containerDef);
}
});
test.afterAll("Delete Test Database", async () => {
await context?.dispose();
});

View File

@@ -18,65 +18,66 @@ function friendlyTime(date) {
async function main() {
const credentials = new AzureCliCredential();
const client = new CosmosDBManagementClient(credentials, subscriptionId);
const accounts = await client.databaseAccounts.list(resourceGroupName);
for (const account of accounts) {
const accounts = client.databaseAccounts.listByResourceGroup(resourceGroupName);
for await (const account of accounts) {
if (account.name.endsWith("-readonly")) {
console.log(`SKIPPED: ${account.name}`);
continue;
}
if (account.kind === "MongoDB") {
const mongoDatabases = await client.mongoDBResources.listMongoDBDatabases(resourceGroupName, account.name);
for (const database of mongoDatabases) {
const mongoDatabases = client.mongoDBResources.listMongoDBDatabases(resourceGroupName, account.name);
for await (const database of mongoDatabases) {
// Unfortunately Mongo does not provide a timestamp in ARM. There is no way to tell how old the DB is other thn encoding it in the ID :(
const timestamp = Number(database.name.split("_").pop());
if (timestamp && timestamp < thirtyMinutesAgo) {
await client.mongoDBResources.deleteMongoDBDatabase(resourceGroupName, account.name, database.name);
await client.mongoDBResources.beginDeleteMongoDBDatabaseAndWait(resourceGroupName, account.name, database.name);
console.log(`DELETED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
} else {
console.log(`SKIPPED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
}
}
} else if (account.capabilities.find((c) => c.name === "EnableCassandra")) {
const cassandraDatabases = await client.cassandraResources.listCassandraKeyspaces(
const cassandraDatabases = client.cassandraResources.listCassandraKeyspaces(
resourceGroupName,
account.name,
);
for (const database of cassandraDatabases) {
const timestamp = Number(database.resource._ts) * 1000;
for await (const database of cassandraDatabases) {
const timestamp = Number(database.resource.ts) * 1000;
if (timestamp && timestamp < thirtyMinutesAgo) {
await client.cassandraResources.deleteCassandraKeyspace(resourceGroupName, account.name, database.name);
await client.cassandraResources.beginDeleteCassandraKeyspaceAndWait(resourceGroupName, account.name, database.name);
console.log(`DELETED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
} else {
console.log(`SKIPPED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
}
}
} else if (account.capabilities.find((c) => c.name === "EnableTable")) {
const tablesDatabase = await client.tableResources.listTables(resourceGroupName, account.name);
for (const database of tablesDatabase) {
const timestamp = Number(database.resource._ts) * 1000;
const tablesDatabase = client.tableResources.listTables(resourceGroupName, account.name);
for await (const database of tablesDatabase) {
const timestamp = Number(database.resource.ts) * 1000;
if (timestamp && timestamp < thirtyMinutesAgo) {
await client.tableResources.deleteTable(resourceGroupName, account.name, database.name);
await client.tableResources.beginDeleteTableAndWait(resourceGroupName, account.name, database.name);
console.log(`DELETED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
} else {
console.log(`SKIPPED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
}
}
} else if (account.capabilities.find((c) => c.name === "EnableGremlin")) {
const graphDatabases = await client.gremlinResources.listGremlinDatabases(resourceGroupName, account.name);
for (const database of graphDatabases) {
const timestamp = Number(database.resource._ts) * 1000;
const graphDatabases = client.gremlinResources.listGremlinDatabases(resourceGroupName, account.name);
for await (const database of graphDatabases) {
const timestamp = Number(database.resource.ts) * 1000;
if (timestamp && timestamp < thirtyMinutesAgo) {
await client.gremlinResources.deleteGremlinDatabase(resourceGroupName, account.name, database.name);
await client.gremlinResources.beginDeleteGremlinDatabaseAndWait(resourceGroupName, account.name, database.name);
console.log(`DELETED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
} else {
console.log(`SKIPPED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
}
}
} else if (account.kind === "GlobalDocumentDB") {
const sqlDatabases = await client.sqlResources.listSqlDatabases(resourceGroupName, account.name);
const sqlDatabasesToDelete = sqlDatabases.map(async (database) => {
await deleteWithRetry(client, database, account.name);
});
const sqlDatabases = client.sqlResources.listSqlDatabases(resourceGroupName, account.name);
const sqlDatabasesToDelete = [];
for await (const database of sqlDatabases) {
sqlDatabasesToDelete.push(deleteWithRetry(client, database, account.name));
}
await Promise.all(sqlDatabasesToDelete);
}
}
@@ -90,9 +91,9 @@ async function deleteWithRetry(client, database, accountName) {
while (attempt < maxRetries) {
try {
const timestamp = Number(database.resource._ts) * 1000;
const timestamp = Number(database.resource.ts) * 1000;
if (timestamp && timestamp < thirtyMinutesAgo) {
await client.sqlResources.deleteSqlDatabase(resourceGroupName, accountName, database.name);
await client.sqlResources.beginDeleteSqlDatabaseAndWait(resourceGroupName, accountName, database.name);
console.log(`DELETED: ${accountName} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
} else {
console.log(`SKIPPED: ${accountName} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
@@ -126,7 +127,6 @@ main()
process.exit(0);
})
.catch((err) => {
console.log(err);
console.log("Cleanup failed! Exiting with success. Cleanup should always fail safe.");
process.exit(0);
console.error(err);
process.exit(1);
});