Test#2647
Open
jainakanksha-msft wants to merge 10 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR modernizes Azurite’s dependencies (notably Azure SDK packages) and updates the codebase/tests to align with newer SDK typings and runtime behaviors (headers access, credential access, typed-array handling, and generated swagger defaults).
Changes:
- Upgrades core dependencies (e.g.,
@azure/ms-rest-js,@azure/storage-blob,@azure/storage-queue,axios,tedious) and adjusts tests accordingly. - Refactors multiple tests and runtime helpers to use
Uint8Array-based buffers and updated SDK option/property names (e.g.,visibilityTimeout,_response.headers.get(...),client.credential). - Updates generated swagger/artifacts to remove the default value for
x-ms-blob-sequence-numberand tightens Express query handling in generated request adapters.
Reviewed changes
Copilot reviewed 30 out of 39 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/testutils.ts | Formats file creation loops; comments out overrideRequest helper. |
| tests/table/apis/table.entity.rest.test.ts | Adds non-null assertions for etag headers. |
| tests/table/apis/table.batch.errorhandling.test.ts | Removes RestError import and loosens error typing; minor formatting. |
| tests/simpleTokenCredential.ts | Switches TokenCredential-related imports to @azure/identity. |
| tests/queue/queueSas.test.ts | Uses (serviceClient as any).credential instead of pipeline factory introspection. |
| tests/queue/queueCorsRequest.test.ts | Asserts CORS headers via _response.headers.get(...) rather than response-shape properties. |
| tests/queue/oauth.test.ts | Updates error detail property casing and cleans up formatting. |
| tests/queue/apis/messages.test.ts | Renames visibilitytimeout to visibilityTimeout; adjusts timing for an expiration test. |
| tests/linuxbinary.test.ts | Comments out the entire linux binary test file. |
| tests/blob/sas.test.ts | Uses client.credential; updates hard-coded expected SAS strings. |
| tests/blob/oauth.test.ts | Updates error detail property casing. |
| tests/blob/fsStore.test.ts | Changes chunk buffering from Buffer[] to Uint8Array[]. |
| tests/blob/blockblob.highlevel.test.ts | Updates buffer comparisons and abort logic for upload stream test. |
| tests/blob/blobCorsRequest.test.ts | Mirrors queue CORS assertion changes using _response.headers.get(...). |
| tests/blob/apis/service.test.ts | Updates expected error detail property casing; uses client.credential. |
| tests/blob/apis/blockblob.test.ts | Simplifies MD5 typed-array construction. |
| tests/blob/apis/blobbatch.test.ts | Uses client.credential for batch SAS signing. |
| tests/blob/apis/blob.test.ts | Removes node/browser branch for MD5 header; normalizes to Uint8Array. |
| swagger/blob.md | Notes removal of default for blob sequence number header parameter. |
| swagger/blob-storage-2021-10-04.json | Removes "default": 0 from BlobSequenceNumber parameter. |
| src/table/generated/utils/xml.ts | Adjusts xml2js parse callback typing; removes builder options. |
| src/table/generated/ExpressRequestAdapter.ts | Ensures getQuery() only returns strings. |
| src/queue/utils/utils.ts | Adjusts xml2js parse callback typing. |
| src/queue/generated/utils/xml.ts | Adjusts xml2js parse callback typing; removes builder options. |
| src/queue/generated/ExpressRequestAdapter.ts | Ensures getQuery() only returns strings. |
| src/common/utils/utils.ts | Normalizes HMAC/MD5 helpers to return/accept Uint8Array. |
| src/blob/persistence/SqlBlobMetadataStore.ts | Formatting tweaks; normalizes restored byte arrays to Uint8Array. |
| src/blob/persistence/LokiBlobMetadataStore.ts | Changes persisted-byte-array restore logic (Buffer/Uint8Array handling). |
| src/blob/main.ts | Minor formatting cleanup. |
| src/blob/handlers/SubResponseTextBodyStream.ts | Reformats class; changes end() typing to return this. |
| src/blob/handlers/BlobBatchSubResponse.ts | Minor formatting updates. |
| src/blob/handlers/BlobBatchHandler.ts | Changes stream-to-buffer copy to use Uint8Array conversion. |
| src/blob/handlers/AppendBlobHandler.ts | Normalizes contentMD5 to Uint8Array. |
| src/blob/generated/utils/xml.ts | Adjusts xml2js parse callback typing; removes builder options. |
| src/blob/generated/ExpressRequestAdapter.ts | Ensures getQuery() only returns strings. |
| src/blob/generated/artifacts/parameters.ts | Removes defaultValue: 0 for blob sequence number header. |
| src/blob/authentication/BlobSharedKeyAuthenticator.ts | Formatting changes plus string-to-sign line reordering. |
| package.json | Upgrades dependencies; removes azure-storage; adds @azure/identity. |
Comments suppressed due to low confidence (1)
src/blob/persistence/LokiBlobMetadataStore.ts:3121
restoreUint8Arrayno longer handles the JSON-serialized Buffer shape ({ type: "Buffer", data: [...] }). Existing Loki DB files created by earlier versions can contain this shape, so this change can break loading persisted metadata after upgrade. Consider keeping backward-compatible handling forobj.type === "Buffer"(usingobj.data) in addition to theBufferinstance case.
private restoreUint8Array(obj: any): Uint8Array | undefined {
if (typeof obj !== "object") {
return undefined;
}
if (obj instanceof Buffer) {
return new Uint8Array(obj);
}
const length = Object.keys(obj).length;
const arr = Buffer.allocUnsafe(length);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+206
to
+217
| // headers: { [key: string]: string }; | ||
| // } = { headers: {} }, | ||
| // service: StorageServiceClient | ||
| // ) { |
| @@ -73,7 +73,6 @@ | |||
| "@typescript-eslint/eslint-plugin": "^5.54.1", | |||
| "@typescript-eslint/parser": "^5.54.1", | |||
| "autorest": "^3.6.0", | |||
Comment on lines
80
to
+81
| this.getHeaderValueToSign(req, HeaderConstants.CONTENT_LANGUAGE), | ||
| this.getHeaderValueToSign(req, HeaderConstants.CONTENT_ENCODING), |
Comment on lines
123
to
+142
| @@ -140,7 +139,7 @@ describe("Shared Access Signature (SAS) authentication", () => { | |||
| storageSharedKeyCredential as StorageSharedKeyCredential | |||
| ).toString(); | |||
|
|
|||
| assert.equal(sas, "sv=2016-05-31&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T13%3A31%3A48Z&se=2022-04-17T13%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=3tOzYrzhkaX48zalU5WlyEJg%2B7Tj4RzY4jBo9mCi8AM%3D"); | |||
| assert.equal(sas, "sv=2016-05-31&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T06%3A31%3A48Z&se=2022-04-17T06%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=bTjnB%2ByT4DkYQcmMfpedew72%2FKdvTMuz29uatYuWYME%3D"); | |||
Comment on lines
+1
to
+5
| // // run "EXE Mocha TS File - Loki" in VS Code to run this test | ||
| // import * as assert from 'assert'; | ||
| // import { execFile } from 'child_process'; | ||
| // import find from 'find-process'; | ||
|
|
Comment on lines
21
to
+23
| if (chunk) this.bodyText += (chunk! as any).toString(); | ||
| this.subResponse.end(); | ||
| return this; |
Comment on lines
+162
to
+163
| assert.strictEqual( | ||
| (err as any).statusCode, |
Comment on lines
532
to
+548
| @@ -544,6 +544,8 @@ describe("Messages APIs test", () => { | |||
| assert.ok(eResult.nextVisibleOn); | |||
| assert.ok(eResult.version); | |||
|
|
|||
| await sleep(1000); | |||
|
|
|||
Comment on lines
+292
to
+298
| export function generateTableSasToken({ | ||
| accountName, | ||
| accountKey, | ||
| tableName, | ||
| permissions, | ||
| expiry | ||
| }: { |
Comment on lines
+394
to
+399
| export function generateTableServiceSasWithIdentifier({ | ||
| accountName, | ||
| accountKey, | ||
| tableName, | ||
| identifier | ||
| }: { |
Comment on lines
+212
to
+252
| // export function overrideRequest( | ||
| // override: { | ||
| // headers: { [key: string]: string }; | ||
| // } = { headers: {} }, | ||
| // service: StorageServiceClient | ||
| // ) { | ||
| // const hasOriginal = !!(service as any).__proto__.__proto__ | ||
| // .__original_buildRequestOptions; | ||
|
|
||
| // const original = hasOriginal | ||
| // ? (service as any).__proto__.__proto__.__original_buildRequestOptions | ||
| // : (service as any).__proto__.__proto__._buildRequestOptions; | ||
|
|
||
| // if (!hasOriginal) { | ||
| // (service as any).__proto__.__proto__.__original_buildRequestOptions = original; | ||
| // } | ||
|
|
||
| // const _buildRequestOptions = original.bind(service); | ||
| // (service as any).__proto__.__proto__._buildRequestOptions = ( | ||
| // webResource: any, | ||
| // body: any, | ||
| // options: any, | ||
| // callback: any | ||
| // ) => { | ||
| // _buildRequestOptions( | ||
| // webResource, | ||
| // body, | ||
| // options, | ||
| // (err: any, finalRequestOptions: any) => { | ||
| // for (const key in override.headers) { | ||
| // if (Object.prototype.hasOwnProperty.call(override.headers, key)) { | ||
| // const element = override.headers[key]; | ||
| // finalRequestOptions.headers[key] = element; | ||
| // } | ||
| // } | ||
|
|
||
| // callback(err, finalRequestOptions); | ||
| // } | ||
| // ); | ||
| // }; | ||
| // } |
Comment on lines
+8
to
16
| public partitionKey: string; | ||
| public rowKey: string; | ||
| public myValue: string; | ||
|
|
||
| constructor(part: string, row: string, value: string) { | ||
| this.PartitionKey = eg.String(part); | ||
| this.RowKey = eg.String(row); | ||
| this.myValue = eg.String(value); | ||
| this.partitionKey = part; | ||
| this.rowKey = row; | ||
| this.myValue = value; | ||
| } |
Comment on lines
1
to
380
| // // run "EXE Mocha TS File - Loki" in VS Code to run this test | ||
| // import * as assert from 'assert'; | ||
| // import { execFile } from 'child_process'; | ||
| // import find from 'find-process'; | ||
|
|
||
| // import { | ||
| // BlobServiceClient, newPipeline as blobNewPipeline, | ||
| // StorageSharedKeyCredential as blobStorageSharedKeyCredential | ||
| // } from '@azure/storage-blob'; | ||
| // import { | ||
| // newPipeline as queueNewPipeline, QueueClient, QueueServiceClient, | ||
| // StorageSharedKeyCredential as queueStorageSharedKeyCredential | ||
| // } from '@azure/storage-queue'; | ||
|
|
||
| // import { configLogger } from '../src/common/Logger'; | ||
| // import { HeaderConstants, TABLE_API_VERSION } from '../src/table/utils/constants'; | ||
| // import BlobTestServerFactory from './BlobTestServerFactory'; | ||
| // import { | ||
| // createConnectionStringForTest, HOST, PORT, PROTOCOL | ||
| // } from './table/utils/table.entity.test.utils'; | ||
| // import { | ||
| // bodyToString, EMULATOR_ACCOUNT_KEY, EMULATOR_ACCOUNT_NAME, getUniqueName, overrideRequest, | ||
| // restoreBuildRequestOptions | ||
| // } from './testutils'; | ||
| // import { existsSync } from 'fs'; | ||
|
|
||
| // // server address used for testing. Note that Azuritelinux has | ||
| // // server address of http://127.0.0.1:10000 and so on by default | ||
| // // and we need to configure them when starting azuritelinux | ||
| // const blobAddress = "http://127.0.0.1:11000"; | ||
| // const queueAddress = "http://127.0.0.1:11001"; | ||
| // const tableAddress = "http://127.0.0.1:11002"; | ||
|
|
||
| // // Set true to enable debug log | ||
| // configLogger(false); | ||
| // // For convenience, we have a switch to control the use | ||
| // // of a local Azurite instance, otherwise we need an | ||
| // // ENV VAR called AZURE_TABLE_STORAGE added to mocha | ||
| // // script or launch.json containing | ||
| // // Azure Storage Connection String (using SAS or Key). | ||
| // const testLocalAzuriteInstance = true; | ||
|
|
||
| // const binaryPath = "./release/azuritelinux" | ||
|
|
||
| // function throwOnMissingBinary() { | ||
| // if (!existsSync(binaryPath)) { | ||
| // throw new Error("The Linux binary does not exist. You must build it first using 'npm run build:linux'.") | ||
| // } | ||
| // } | ||
|
|
||
| // describe("linux binary test", () => { | ||
|
|
||
| // const tableService = Azure.createTableService( | ||
| // createConnectionStringForTest(testLocalAzuriteInstance) | ||
| // ); | ||
| // tableService.enableGlobalHttpAgent = true; | ||
|
|
||
| // let tableName: string = getUniqueName("table"); | ||
|
|
||
| // const requestOverride = { headers: {} }; | ||
|
|
||
| // let childPid: number; | ||
|
|
||
| // beforeEach(() => throwOnMissingBinary()) | ||
|
|
||
| // before(async () => { | ||
| // throwOnMissingBinary() | ||
|
|
||
| // overrideRequest(requestOverride, tableService); | ||
| // tableName = getUniqueName("table"); | ||
| // const child = execFile(binaryPath, ["--blobPort 11000", "--queuePort 11001", "--tablePort 11002"], { cwd: process.cwd(), shell: true, env: {} }); | ||
|
|
||
| // childPid = child.pid; | ||
|
|
||
| // const fullSuccessMessage = "Azurite Blob service is starting at " + blobAddress + "\nAzurite Blob service is successfully listening at " + blobAddress + | ||
| // "\nAzurite Queue service is starting at " + queueAddress + "\nAzurite Queue service is successfully listening at " + queueAddress + | ||
| // "\nAzurite Table service is starting at " + tableAddress + "\nAzurite Table service is successfully listening at " + tableAddress + "\n"; | ||
| // let messageReceived: string = ""; | ||
|
|
||
| // function stdoutOn() { | ||
| // return new Promise(resolve => { | ||
| // // exclamation mark suppresses the TS error that "child.stdout is possibly null" | ||
| // child.stdout!.on('data', function (data: any) { | ||
| // messageReceived += data.toString(); | ||
| // if (messageReceived == fullSuccessMessage) { | ||
| // resolve("resolveMessage"); | ||
| // } | ||
| // }); | ||
| // }); | ||
| // } | ||
|
|
||
| // await stdoutOn(); | ||
| // }); | ||
|
|
||
| // after(async () => { | ||
| // // TO DO | ||
| // // Currently, the mocha test does not quit unless "--exit" is added to the mocha command | ||
| // // The current fix is to have "--exit" added but the issue causing mocha to be unable to | ||
| // // quit has not been identified | ||
| // restoreBuildRequestOptions(tableService); | ||
| // tableService.removeAllListeners(); | ||
|
|
||
| // await find('name', 'azuritelinux', true).then((list: any) => { | ||
| // if (list.length > 0) { | ||
| // process.kill(list[0].pid); | ||
| // } | ||
| // }); | ||
|
|
||
| // if (childPid) { | ||
| // process.kill(childPid); | ||
| // } | ||
| // }); | ||
|
|
||
| // describe("table test", () => { | ||
| // it("createTable, prefer=return-no-content, accept=application/json;odata=minimalmetadata @loki", (done) => { | ||
| // /* Azure Storage Table SDK doesn't support customize Accept header and Prefer header, | ||
| // thus we workaround this by override request headers to test following 3 OData levels responses. | ||
| // - application/json;odata=nometadata | ||
| // - application/json;odata=minimalmetadata | ||
| // - application/json;odata=fullmetadata | ||
| // */ | ||
| // requestOverride.headers = { | ||
| // Prefer: "return-no-content", | ||
| // accept: "application/json;odata=minimalmetadata" | ||
| // }; | ||
|
|
||
| // tableService.createTable(tableName, (error, result, response) => { | ||
| // if (!error) { | ||
| // assert.strictEqual(result.TableName, tableName); | ||
| // assert.strictEqual(result.statusCode, 204); | ||
| // const headers = response.headers!; | ||
| // assert.strictEqual(headers["x-ms-version"], TABLE_API_VERSION); | ||
| // assert.deepStrictEqual(response.body, ""); | ||
| // } | ||
| // done(); | ||
| // }); | ||
| // }); | ||
|
|
||
| // it("queryTable, accept=application/json;odata=minimalmetadata @loki", (done) => { | ||
| // /* Azure Storage Table SDK doesn't support customize Accept header and Prefer header, | ||
| // thus we workaround this by override request headers to test following 3 OData levels responses. | ||
| // - application/json;odata=nometadata | ||
| // - application/json;odata=minimalmetadata | ||
| // - application/json;odata=fullmetadata | ||
| // */ | ||
| // requestOverride.headers = { | ||
| // accept: "application/json;odata=minimalmetadata" | ||
| // }; | ||
|
|
||
| // tableService.listTablesSegmented(null as any, (error, result, response) => { | ||
| // if (!error) { | ||
| // assert.strictEqual(response.statusCode, 200); | ||
| // const headers = response.headers!; | ||
| // assert.strictEqual(headers["x-ms-version"], TABLE_API_VERSION); | ||
| // const bodies = response.body! as any; | ||
| // assert.deepStrictEqual( | ||
| // bodies["odata.metadata"], | ||
| // `${PROTOCOL}://${HOST}:${PORT}/${EMULATOR_ACCOUNT_NAME}/$metadata#Tables` | ||
| // ); | ||
| // assert.ok(bodies.value[0].TableName); | ||
| // } | ||
| // done(); | ||
| // }); | ||
| // }); | ||
|
|
||
| // it("deleteTable that exists, @loki", (done) => { | ||
| // /* | ||
| // https://docs.microsoft.com/en-us/rest/api/storageservices/delete-table | ||
| // */ | ||
| // requestOverride.headers = {}; | ||
|
|
||
| // const tableToDelete = tableName + "del"; | ||
|
|
||
| // tableService.createTable(tableToDelete, (error, result, response) => { | ||
| // if (!error) { | ||
| // tableService.deleteTable(tableToDelete, (deleteError, deleteResult) => { | ||
| // if (!deleteError) { | ||
| // // no body expected, we expect 204 no content on successful deletion | ||
| // assert.strictEqual(deleteResult.statusCode, 204); | ||
| // } else { | ||
| // assert.ifError(deleteError); | ||
| // } | ||
| // done(); | ||
| // }); | ||
| // } else { | ||
| // assert.fail("Test failed to create the table"); | ||
| // done(); | ||
| // } | ||
| // }); | ||
| // }); | ||
|
|
||
| // it("deleteTable that does not exist, @loki", (done) => { | ||
| // // https://docs.microsoft.com/en-us/rest/api/storageservices/delete-table | ||
| // requestOverride.headers = {}; | ||
|
|
||
| // const tableToDelete = tableName + "causeerror"; | ||
|
|
||
| // tableService.deleteTable(tableToDelete, (error, result) => { | ||
| // assert.strictEqual(result.statusCode, 404); // no body expected, we expect 404 | ||
| // const storageError = error as any; | ||
| // assert.strictEqual(storageError.code, "ResourceNotFound"); | ||
| // done(); | ||
| // }); | ||
| // }); | ||
|
|
||
| // it("createTable with invalid version, @loki", (done) => { | ||
| // requestOverride.headers = { [HeaderConstants.X_MS_VERSION]: "invalid" }; | ||
|
|
||
| // tableService.createTable("test", (error, result) => { | ||
| // assert.strictEqual(result.statusCode, 400); | ||
| // done(); | ||
| // }); | ||
| // }); | ||
| // }); | ||
|
|
||
| // describe("blob test", () => { | ||
| // const factory = new BlobTestServerFactory(); | ||
| // const blobServer = factory.createServer(); | ||
|
|
||
| // const blobBaseURL = `http://${blobServer.config.host}:${blobServer.config.port}/devstoreaccount1`; | ||
| // const blobServiceClient = new BlobServiceClient( | ||
| // blobBaseURL, | ||
| // blobNewPipeline( | ||
| // new blobStorageSharedKeyCredential( | ||
| // EMULATOR_ACCOUNT_NAME, | ||
| // EMULATOR_ACCOUNT_KEY | ||
| // ), | ||
| // { | ||
| // retryOptions: { maxTries: 1 }, | ||
| // // Make sure socket is closed once the operation is done. | ||
| // keepAliveOptions: { enable: false } | ||
| // } | ||
| // ) | ||
| // ); | ||
|
|
||
| // let containerName: string = getUniqueName("container"); | ||
| // let containerClient = blobServiceClient.getContainerClient(containerName); | ||
| // let blobName: string = getUniqueName("blob"); | ||
| // let blobClient = containerClient.getBlobClient(blobName); | ||
| // let blockBlobClient = blobClient.getBlockBlobClient(); | ||
| // const content = "Hello World"; | ||
|
|
||
| // beforeEach(async () => { | ||
| // containerName = getUniqueName("container"); | ||
| // containerClient = blobServiceClient.getContainerClient(containerName); | ||
| // await containerClient.create(); | ||
| // blobName = getUniqueName("blob"); | ||
| // blobClient = containerClient.getBlobClient(blobName); | ||
| // blockBlobClient = blobClient.getBlockBlobClient(); | ||
| // await blockBlobClient.upload(content, content.length); | ||
| // }); | ||
|
|
||
| // afterEach(async () => { | ||
| // await containerClient.delete(); | ||
| // }); | ||
| // it("download with default parameters @loki @sql", async () => { | ||
| // const result = await blobClient.download(0); | ||
| // assert.deepStrictEqual(await bodyToString(result, content.length), content); | ||
| // assert.equal(result.contentRange, undefined); | ||
| // assert.equal( | ||
| // result._response.request.headers.get("x-ms-client-request-id"), | ||
| // result.clientRequestId | ||
| // ); | ||
| // }); | ||
|
|
||
| // it("download should work with conditional headers @loki @sql", async () => { | ||
| // const properties = await blobClient.getProperties(); | ||
| // const result = await blobClient.download(0, undefined, { | ||
| // conditions: { | ||
| // ifMatch: properties.etag, | ||
| // ifNoneMatch: "invalidetag", | ||
| // ifModifiedSince: new Date("2018/01/01"), | ||
| // ifUnmodifiedSince: new Date("2188/01/01") | ||
| // } | ||
| // }); | ||
| // assert.deepStrictEqual(await bodyToString(result, content.length), content); | ||
| // assert.equal(result.contentRange, undefined); | ||
| // assert.equal( | ||
| // result._response.request.headers.get("x-ms-client-request-id"), | ||
| // result.clientRequestId | ||
| // ); | ||
| // }); | ||
| // }); | ||
|
|
||
| // describe("queue test", () => { | ||
| // // TODO: Create a server factory as tests utils | ||
| // const host = "127.0.0.1"; | ||
| // const port = 11001; | ||
|
|
||
| // const baseURL = `http://${host}:${port}/devstoreaccount1`; | ||
| // const serviceClient = new QueueServiceClient( | ||
| // baseURL, | ||
| // queueNewPipeline( | ||
| // new queueStorageSharedKeyCredential( | ||
| // EMULATOR_ACCOUNT_NAME, | ||
| // EMULATOR_ACCOUNT_KEY | ||
| // ), | ||
| // { | ||
| // retryOptions: { maxTries: 1 } | ||
| // } | ||
| // ) | ||
| // ); | ||
|
|
||
| // let queueName: string; | ||
| // let queueClient: QueueClient; | ||
|
|
||
| // beforeEach(async () => { | ||
| // queueName = getUniqueName("queue"); | ||
| // queueClient = serviceClient.getQueueClient(queueName); | ||
| // await queueClient.create(); | ||
| // }); | ||
|
|
||
| // afterEach(async () => { | ||
| // await queueClient.delete(); | ||
| // }); | ||
|
|
||
| // it("setMetadata @loki", async () => { | ||
| // const metadata = { | ||
| // key0: "val0", | ||
| // keya: "vala", | ||
| // keyb: "valb" | ||
| // }; | ||
| // const mResult = await queueClient.setMetadata(metadata); | ||
| // assert.equal( | ||
| // mResult._response.request.headers.get("x-ms-client-request-id"), | ||
| // mResult.clientRequestId | ||
| // ); | ||
|
|
||
| // const result = await queueClient.getProperties(); | ||
| // assert.deepEqual(result.metadata, metadata); | ||
| // assert.equal( | ||
| // result._response.request.headers.get("x-ms-client-request-id"), | ||
| // result.clientRequestId | ||
| // ); | ||
| // }); | ||
|
|
||
| // it("getProperties with default/all parameters @loki", async () => { | ||
| // const result = await queueClient.getProperties(); | ||
| // assert.ok(result.approximateMessagesCount! >= 0); | ||
| // assert.ok(result.requestId); | ||
| // assert.ok(result.version); | ||
| // assert.ok(result.date); | ||
| // }); | ||
|
|
||
| // it("SetAccessPolicy should work @loki", async () => { | ||
| // const queueAcl = [ | ||
| // { | ||
| // accessPolicy: { | ||
| // expiresOn: new Date("2018-12-31T11:22:33.4567890Z"), | ||
| // permissions: "raup", | ||
| // startsOn: new Date("2017-12-31T11:22:33.4567890Z") | ||
| // }, | ||
| // id: "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=" | ||
| // }, | ||
| // { | ||
| // accessPolicy: { | ||
| // expiresOn: new Date("2030-11-31T11:22:33.4567890Z"), | ||
| // permissions: "a", | ||
| // startsOn: new Date("2017-12-31T11:22:33.4567890Z") | ||
| // }, | ||
| // id: "policy2" | ||
| // } | ||
| // ]; | ||
|
|
||
| // const sResult = await queueClient.setAccessPolicy(queueAcl); | ||
| // assert.equal( | ||
| // sResult._response.request.headers.get("x-ms-client-request-id"), | ||
| // sResult.clientRequestId | ||
| // ); | ||
|
|
||
| // const result = await queueClient.getAccessPolicy(); | ||
| // assert.deepEqual(result.signedIdentifiers, queueAcl); | ||
| // assert.equal( | ||
| // result._response.request.headers.get("x-ms-client-request-id"), | ||
| // result.clientRequestId | ||
| // ); | ||
| // }); | ||
| // }); | ||
| // }); | ||
|
|
Comment on lines
142
to
+174
| @@ -156,7 +155,7 @@ describe("Shared Access Signature (SAS) authentication", () => { | |||
| storageSharedKeyCredential as StorageSharedKeyCredential | |||
| ).toString(); | |||
|
|
|||
| assert.equal(sas2, "sv=2018-11-09&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T13%3A31%3A48Z&se=2022-04-17T13%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=o23T5PzZn4Daklb%2F8Ef25%2FUprkIIeq4zI4QxT57iim8%3D"); | |||
| assert.equal(sas2, "sv=2018-11-09&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T06%3A31%3A48Z&se=2022-04-17T06%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=zty6%2BwJgp86CuAlzq9w%2F%2BavVwjoEHgBOTXXmN7xAyio%3D"); | |||
|
|
|||
| const sas3 = generateAccountSASQueryParameters( | |||
| { | |||
| @@ -172,7 +171,7 @@ describe("Shared Access Signature (SAS) authentication", () => { | |||
| storageSharedKeyCredential as StorageSharedKeyCredential | |||
| ).toString(); | |||
|
|
|||
| assert.equal(sas3, "sv=2020-12-06&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T13%3A31%3A48Z&se=2022-04-17T13%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=zbYgTg6EQCUeDmU4CbXEE6nMA7jA4E7d%2FXBVd7rifng%3D"); | |||
| assert.equal(sas3, "sv=2020-12-06&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T06%3A31%3A48Z&se=2022-04-17T06%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=QB5jKCJTqn7jcPidkkXAazBQBL6VDMY7HumUIM78ROE%3D"); | |||
Comment on lines
+307
to
+335
| const stringToSign = | ||
| permissions + | ||
| "\n" + | ||
| "" + | ||
| "\n" + // start time (optional) | ||
| expiry + | ||
| "\n" + | ||
| `/table/${accountName}/${tableName}` + | ||
| "\n" + | ||
| "" + | ||
| "\n" + | ||
| "" + | ||
| "\n" + | ||
| "https,http\n" + // protocol | ||
| version; | ||
|
|
||
| const signature = crypto | ||
| .createHmac("sha256", Uint8Array.from(Buffer.from(accountKey, "base64"))) | ||
| .update(stringToSign, "utf8") | ||
| .digest("base64"); | ||
|
|
||
| return ( | ||
| `sv=${version}` + | ||
| `&tn=${tableName}` + | ||
| `&sp=${permissions}` + | ||
| `&se=${encodeURIComponent(expiry)}` + | ||
| `&sig=${encodeURIComponent(signature)}` | ||
| ); | ||
| } |
Comment on lines
+60
to
67
| try { | ||
| await tableService.getStatistics(); | ||
| assert.fail("Expected error"); | ||
| } catch (err: any) { | ||
| assert.strictEqual(err.statusCode, 400); | ||
| assert.strictEqual(err.code, "InvalidQueryParameterValue"); | ||
| assert.ok(err); | ||
| }); | ||
| } | ||
| }); |
Comment on lines
+115
to
121
| it("createTable, prefer=return-content, accept=application/json;odata=minimalmetadata @loki", async () => { | ||
| // TODO | ||
| done(); | ||
| }); | ||
|
|
||
| it("createTable, prefer=return-content, accept=application/json;odata=nometadata @loki", (done) => { | ||
| it("createTable, prefer=return-content, accept=application/json;odata=nometadata @loki", async () => { | ||
| // TODO | ||
| done(); | ||
| }); |
Comment on lines
+311
to
330
| it("Should have a valid OData Metadata value when inserting a table, @loki", async () => { | ||
| const capture: { headers?: any; body?: any; status?: number } = {}; | ||
| const requestHeaders = { | ||
| Prefer: "return-content", | ||
| Accept: "application/json;odata=fullmetadata" | ||
| }; | ||
| const aclTableName: string = tableName + "setAcl"; | ||
| tableService.createTable(aclTableName, (error, result, response) => { | ||
| if (error) { | ||
| const storageErr = error as StorageError; | ||
| assert.strictEqual(storageErr.statusCode, 409, "TableDidNotExist"); | ||
| const client = TableServiceClient.fromConnectionString( | ||
| createConnectionStringForTest(testLocalAzuriteInstance), | ||
| { | ||
| allowInsecureConnection: testLocalAzuriteInstance, | ||
| httpClient: createHttpClientForTest(requestHeaders, capture) | ||
| } | ||
| ); | ||
| const newTableName: string = getUniqueName("table"); | ||
|
|
||
| // a random id used to test whether response returns the client id sent in request | ||
| const setClientRequestId = "b86e2b01-a7b5-4df2-b190-205a0c24bd36"; | ||
|
|
||
| tableService.setTableAcl( | ||
| aclTableName, | ||
| tableAcl, | ||
| { clientRequestId: setClientRequestId }, | ||
| (error2, result2, response2) => { | ||
| if (error2) { | ||
| assert.ifError(error2); | ||
| } | ||
| if (response2.headers) { | ||
| assert.strictEqual( | ||
| response2.headers["x-ms-client-request-id"], | ||
| setClientRequestId | ||
| ); | ||
| } | ||
|
|
||
| // tslint:disable-next-line: no-shadowed-variable | ||
| tableService.getTableAcl( | ||
| aclTableName, | ||
| { clientRequestId: setClientRequestId }, | ||
| (error3, result3, response3) => { | ||
| if (error3) { | ||
| assert.ifError(error3); | ||
| } | ||
|
|
||
| if (response3.headers) { | ||
| assert.strictEqual( | ||
| response3.headers["x-ms-client-request-id"], | ||
| setClientRequestId | ||
| ); | ||
| } | ||
|
|
||
| assert.deepStrictEqual(result3.signedIdentifiers, tableAcl); | ||
| done(); | ||
| } | ||
| ); | ||
| } | ||
| ); | ||
| }); | ||
| await client.createTable(newTableName); | ||
| const body = capture.body as any; | ||
| const meta: string = body["odata.metadata" as keyof object]; | ||
| assert.strictEqual(meta.endsWith("/@Element"), true); | ||
| }); |
Comment on lines
+588
to
+638
| it("10. Upsert succeeds with Update permission, @loki", async () => { | ||
| const tableName: string = getUniqueName("sas10"); | ||
| tableService.createTable(tableName, (error, result, response) => { | ||
| // created table for tests | ||
| const sasService = getSasService( | ||
| { | ||
| Permissions: TableSASPermission.Update, | ||
| ...sasPeriod(0, 5) | ||
| }, | ||
| tableName | ||
| ); | ||
|
|
||
| // this upserts, so we expect success | ||
| sasService.insertOrReplaceEntity( | ||
| tableName, | ||
| { PartitionKey: "part1", RowKey: "row4", myValue: "newValue" }, | ||
| (updateError, updateResult, updateResponse) => { | ||
| if (updateError) { | ||
| const castUpdateStatusCode = (updateError as StorageError) | ||
| .statusCode; | ||
| assert.fail( | ||
| "Test failed and had HTTP error : " + castUpdateStatusCode | ||
| ); | ||
| } else { | ||
| assert.strictEqual( | ||
| updateResponse.statusCode, | ||
| 204, | ||
| "We did not get the expected status code : " + | ||
| updateResponse.statusCode | ||
| ); | ||
| } | ||
| done(); | ||
| } | ||
| ); | ||
| const conn = createConnectionStringForTest(testLocalAzuriteInstance); | ||
| const accountName = /AccountName=([^;]*)/.exec(conn)![1]; | ||
| const accountKey = /AccountKey=([^;]*)/.exec(conn)![1]; | ||
| const baseUrl = getBaseUrlForTest(); | ||
|
|
||
| const adminClient = TableClient.fromConnectionString(conn, tableName, { | ||
| allowInsecureConnection: testLocalAzuriteInstance | ||
| }); | ||
|
|
||
| await adminClient.createTable(); | ||
|
|
||
| const now = Date.now(); | ||
| const start = new Date(now).toISOString(); | ||
| const expiry = new Date(now + 5 * 60 * 1000).toISOString(); | ||
|
|
||
| const sas = generateTableAccountSasToken({ | ||
| accountName, | ||
| accountKey, | ||
| permissions: "u", | ||
| start, | ||
| expiry | ||
| }); | ||
|
|
||
| const sasClient = new TableClient( | ||
| baseUrl, | ||
| tableName, | ||
| new AzureSASCredential(sas), | ||
| { | ||
| allowInsecureConnection: testLocalAzuriteInstance | ||
| } | ||
| ); | ||
|
|
||
| await sasClient.upsertEntity( | ||
| { | ||
| partitionKey: "part1", | ||
| rowKey: "row4", | ||
| myValue: "newValue" | ||
| }, | ||
| "Replace" | ||
| ); | ||
|
|
||
| // validate that the entity now exists | ||
| const stored = await adminClient.getEntity("part1", "row4"); | ||
|
|
||
| assert.strictEqual(stored.partitionKey, "part1"); | ||
| assert.strictEqual(stored.rowKey, "row4"); | ||
| assert.strictEqual(stored.myValue, "newValue"); | ||
| }); |
Comment on lines
+278
to
+288
| capture.status = response.status; | ||
| capture.headers = response.headers.toJSON(); | ||
|
|
||
| try { | ||
| capture.body = JSON.parse(response.bodyAsText ?? ""); | ||
| } catch { | ||
| capture.body = response.bodyAsText; | ||
| } | ||
|
|
||
| return response; | ||
| } |
Comment on lines
80
to
+81
| this.getHeaderValueToSign(req, HeaderConstants.CONTENT_LANGUAGE), | ||
| this.getHeaderValueToSign(req, HeaderConstants.CONTENT_ENCODING), |
Comment on lines
+328
to
+334
| return ( | ||
| `sv=${version}` + | ||
| `&tn=${tableName}` + | ||
| `&sp=${permissions}` + | ||
| `&se=${encodeURIComponent(expiry)}` + | ||
| `&sig=${encodeURIComponent(signature)}` | ||
| ); |
Comment on lines
+113
to
118
| const sas = generateTableAccountSasToken({ | ||
| accountName, | ||
| accountKey, | ||
| permissions: "a", // ✅ Add only | ||
| expiry | ||
| }); |
Comment on lines
+588
to
+638
| it("10. Upsert succeeds with Update permission, @loki", async () => { | ||
| const tableName: string = getUniqueName("sas10"); | ||
| tableService.createTable(tableName, (error, result, response) => { | ||
| // created table for tests | ||
| const sasService = getSasService( | ||
| { | ||
| Permissions: TableSASPermission.Update, | ||
| ...sasPeriod(0, 5) | ||
| }, | ||
| tableName | ||
| ); | ||
|
|
||
| // this upserts, so we expect success | ||
| sasService.insertOrReplaceEntity( | ||
| tableName, | ||
| { PartitionKey: "part1", RowKey: "row4", myValue: "newValue" }, | ||
| (updateError, updateResult, updateResponse) => { | ||
| if (updateError) { | ||
| const castUpdateStatusCode = (updateError as StorageError) | ||
| .statusCode; | ||
| assert.fail( | ||
| "Test failed and had HTTP error : " + castUpdateStatusCode | ||
| ); | ||
| } else { | ||
| assert.strictEqual( | ||
| updateResponse.statusCode, | ||
| 204, | ||
| "We did not get the expected status code : " + | ||
| updateResponse.statusCode | ||
| ); | ||
| } | ||
| done(); | ||
| } | ||
| ); | ||
| const conn = createConnectionStringForTest(testLocalAzuriteInstance); | ||
| const accountName = /AccountName=([^;]*)/.exec(conn)![1]; | ||
| const accountKey = /AccountKey=([^;]*)/.exec(conn)![1]; | ||
| const baseUrl = getBaseUrlForTest(); | ||
|
|
||
| const adminClient = TableClient.fromConnectionString(conn, tableName, { | ||
| allowInsecureConnection: testLocalAzuriteInstance | ||
| }); | ||
|
|
||
| await adminClient.createTable(); | ||
|
|
||
| const now = Date.now(); | ||
| const start = new Date(now).toISOString(); | ||
| const expiry = new Date(now + 5 * 60 * 1000).toISOString(); | ||
|
|
||
| const sas = generateTableAccountSasToken({ | ||
| accountName, | ||
| accountKey, | ||
| permissions: "u", | ||
| start, | ||
| expiry | ||
| }); | ||
|
|
||
| const sasClient = new TableClient( | ||
| baseUrl, | ||
| tableName, | ||
| new AzureSASCredential(sas), | ||
| { | ||
| allowInsecureConnection: testLocalAzuriteInstance | ||
| } | ||
| ); | ||
|
|
||
| await sasClient.upsertEntity( | ||
| { | ||
| partitionKey: "part1", | ||
| rowKey: "row4", | ||
| myValue: "newValue" | ||
| }, | ||
| "Replace" | ||
| ); | ||
|
|
||
| // validate that the entity now exists | ||
| const stored = await adminClient.getEntity("part1", "row4"); | ||
|
|
||
| assert.strictEqual(stored.partitionKey, "part1"); | ||
| assert.strictEqual(stored.rowKey, "row4"); | ||
| assert.strictEqual(stored.myValue, "newValue"); | ||
| }); |
Comment on lines
+1
to
+25
| // // run "EXE Mocha TS File - Loki" in VS Code to run this test | ||
| // import * as assert from 'assert'; | ||
| // import { execFile } from 'child_process'; | ||
| // import find from 'find-process'; | ||
|
|
||
| // import { | ||
| // BlobServiceClient, newPipeline as blobNewPipeline, | ||
| // StorageSharedKeyCredential as blobStorageSharedKeyCredential | ||
| // } from '@azure/storage-blob'; | ||
| // import { | ||
| // newPipeline as queueNewPipeline, QueueClient, QueueServiceClient, | ||
| // StorageSharedKeyCredential as queueStorageSharedKeyCredential | ||
| // } from '@azure/storage-queue'; | ||
|
|
||
| // import { configLogger } from '../src/common/Logger'; | ||
| // import { HeaderConstants, TABLE_API_VERSION } from '../src/table/utils/constants'; | ||
| // import BlobTestServerFactory from './BlobTestServerFactory'; | ||
| // import { | ||
| // createConnectionStringForTest, HOST, PORT, PROTOCOL | ||
| // } from './table/utils/table.entity.test.utils'; | ||
| // import { | ||
| // bodyToString, EMULATOR_ACCOUNT_KEY, EMULATOR_ACCOUNT_NAME, getUniqueName, overrideRequest, | ||
| // restoreBuildRequestOptions | ||
| // } from './testutils'; | ||
| // import { existsSync } from 'fs'; |
Comment on lines
142
to
+174
| @@ -156,7 +155,7 @@ describe("Shared Access Signature (SAS) authentication", () => { | |||
| storageSharedKeyCredential as StorageSharedKeyCredential | |||
| ).toString(); | |||
|
|
|||
| assert.equal(sas2, "sv=2018-11-09&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T13%3A31%3A48Z&se=2022-04-17T13%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=o23T5PzZn4Daklb%2F8Ef25%2FUprkIIeq4zI4QxT57iim8%3D"); | |||
| assert.equal(sas2, "sv=2018-11-09&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T06%3A31%3A48Z&se=2022-04-17T06%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=zty6%2BwJgp86CuAlzq9w%2F%2BavVwjoEHgBOTXXmN7xAyio%3D"); | |||
|
|
|||
| const sas3 = generateAccountSASQueryParameters( | |||
| { | |||
| @@ -172,7 +171,7 @@ describe("Shared Access Signature (SAS) authentication", () => { | |||
| storageSharedKeyCredential as StorageSharedKeyCredential | |||
| ).toString(); | |||
|
|
|||
| assert.equal(sas3, "sv=2020-12-06&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T13%3A31%3A48Z&se=2022-04-17T13%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=zbYgTg6EQCUeDmU4CbXEE6nMA7jA4E7d%2FXBVd7rifng%3D"); | |||
| assert.equal(sas3, "sv=2020-12-06&ss=btqf&srt=sco&spr=https%2Chttp&st=2022-04-16T06%3A31%3A48Z&se=2022-04-17T06%3A31%3A48Z&sip=0.0.0.0-255.255.255.255&sp=rwdlacup&sig=QB5jKCJTqn7jcPidkkXAazBQBL6VDMY7HumUIM78ROE%3D"); | |||
Comment on lines
23
to
+52
| @@ -36,7 +36,7 @@ | |||
| "rimraf": "^3.0.2", | |||
| "sequelize": "^6.31.0", | |||
| "stoppable": "^1.1.0", | |||
| "tedious": "^16.7.0", | |||
| "tedious": "^18.6.1", | |||
| "to-readable-stream": "^2.1.0", | |||
| "tslib": "^2.3.0", | |||
| "uri-templates": "^0.2.0", | |||
| @@ -48,8 +48,8 @@ | |||
| "@azure/core-auth": "^1.3.2", | |||
| "@azure/core-rest-pipeline": "^1.2.0", | |||
| "@azure/data-tables": "^13.0.1", | |||
| "@azure/storage-blob": "^12.9.0", | |||
| "@azure/storage-queue": "^12.8.0", | |||
| "@azure/storage-blob": "^12.28.0", | |||
| "@azure/storage-queue": "^12.27.0", | |||
…oll.findOne() query and the necessary checks,
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.