import {describe, expect, test} from 'bun:test' import {AzureNoSqlAdapter} from './AzureNoSqlAdapter' import type {AzureRuntimeClient, AzureRuntimeFetchOptions} from '../azure' describe('AzureNoSqlAdapter', () => { test('normalizes Cosmos databases as cloud NoSQL resources', async () => { const adapter = new AzureNoSqlAdapter(testClient({ '/dbs': { _count: 1, Databases: [{id: 'appdb', _rid: 'db-rid', _etag: '"etag"', _ts: 1779307200}], }, })) await expect(adapter.list()).resolves.toEqual([{ id: 'appdb', name: 'appdb', cloud: 'azure', service: 'nosql', type: 'cosmos-database', region: null, createdAt: '2026-05-20T20:00:00.000Z', status: 'available', engine: 'cosmos-nosql', version: 'NoSQL API', instanceClass: null, metadata: { provider: 'azure', databaseService: 'cosmos', api: 'nosql', resourceId: 'db-rid', self: undefined, etag: 'etag', }, }]) }) test('lists containers and documents from the selected database', async () => { const adapter = new AzureNoSqlAdapter(testClient({ '/dbs/appdb/colls': { _count: 1, DocumentCollections: [{ id: 'items', _rid: 'coll-rid', _etag: '"coll-etag"', _ts: 1779307200, partitionKey: {paths: ['/category'], kind: 'Hash'}, }], }, '/dbs/appdb/colls/items': { id: 'items', partitionKey: {paths: ['/category'], kind: 'Hash'}, }, '/dbs/appdb/colls/items/docs': { _count: 1, Documents: [{id: 'item-1', category: 'demo', _etag: '"doc-etag"', _ts: 1779307201}], }, })) await expect(adapter.listCosmosContainers('appdb')).resolves.toMatchObject([{ id: 'items', databaseId: 'appdb', partitionKeyPath: '/category', }]) await expect(adapter.listCosmosItems('appdb', 'items')).resolves.toMatchObject([{ id: 'item-1', databaseId: 'appdb', containerId: 'items', partitionKey: 'demo', etag: 'doc-etag', document: {id: 'item-1', category: 'demo', _etag: '"doc-etag"', _ts: 1779307201}, }]) }) test('sends document partition key when deleting an item', async () => { const calls: Array<{path: string; init: RequestInit}> = [] const adapter = new AzureNoSqlAdapter(testClient({ '/dbs/appdb/colls/items/docs/item-1': null, }, calls)) await adapter.deleteCosmosItem('appdb', 'items', 'item-1', 'demo') expect(calls[0].path).toBe('/dbs/appdb/colls/items/docs/item-1') expect(calls[0].init.method).toBe('DELETE') expect(calls[0].init.headers).toMatchObject({'x-ms-documentdb-partitionkey': '["demo"]'}) }) test('queries documents using the Cosmos SQL query endpoint', async () => { const calls: Array<{path: string; init: RequestInit}> = [] const adapter = new AzureNoSqlAdapter(testClient({ '/dbs/appdb/colls/items/docs': { _count: 1, Documents: [{id: 'item-1'}], }, }, calls)) await expect(adapter.queryCosmosItems('appdb', 'items', 'SELECT * FROM c')).resolves.toEqual({ count: 1, items: [{id: 'item-1'}], }) expect(calls[0].path).toBe('/dbs/appdb/colls/items/docs') expect(calls[0].init.method).toBe('POST') expect(calls[0].init.headers).toMatchObject({'x-ms-documentdb-isquery': 'True'}) }) test('falls back to account-suffixed path when root routing is not implemented', async () => { const calls: Array<{path: string; init: RequestInit}> = [] const adapter = new AzureNoSqlAdapter(testClient({ '/dbs': 'not-implemented', '/devstoreaccount1-cosmos/dbs': { _count: 1, Databases: [{id: 'fallbackdb'}], }, }, calls)) await expect(adapter.list()).resolves.toMatchObject([{id: 'fallbackdb'}]) expect(calls.map((call) => call.path).filter((path) => path.endsWith('/dbs'))).toEqual(['/dbs', '/devstoreaccount1-cosmos/dbs']) }) test('falls back to named NoSQL engine path after root and default account routes fail', async () => { const calls: Array<{path: string; init: RequestInit}> = [] const adapter = new AzureNoSqlAdapter(testClient({ '/dbs': 'not-implemented', '/devstoreaccount1-cosmos/dbs': 'not-implemented', '/devstoreaccount1-cosmos-nosql/dbs': { _count: 1, Databases: [{id: 'nosqldb'}], }, }, calls)) await expect(adapter.list()).resolves.toMatchObject([{id: 'nosqldb'}]) expect(calls.map((call) => call.path).filter((path) => path.endsWith('/dbs'))).toEqual([ '/dbs', '/devstoreaccount1-cosmos/dbs', '/devstoreaccount1-cosmos-nosql/dbs', ]) }) }) function testClient(responses: Record, calls: Array<{path: string; init: RequestInit}> = []): AzureRuntimeClient { return { endpoint: 'http://localhost:4577', accountName: 'devstoreaccount1', async fetch(path: string, init: RequestInit, options: AzureRuntimeFetchOptions = {}) { calls.push({path, init}) if (responses[path] === 'not-implemented') { throw new Error('Azure runtime request failed: HTTP 501') } if (!(path in responses)) { if (options.emptyOnNotFound) return null return new Response('Not Found', {status: 404}) } return new Response(JSON.stringify(responses[path]), {status: 200, headers: {'content-type': 'application/json'}}) }, } }