import { describe, it, expect } from 'vitest' import { computeContentHash, buildProvisioningCode, parseProvisioningCode, } from '../../../app/utils/provisioning_code.js' import type { Configuration } from '../../../app/domain/configuration/configuration.js' import { PolicyType } from '../../../app/domain/configuration/configuration.js' function makeConfig(overrides: Partial = {}): Configuration { return { identifier: 'BM632w-standard', name: 'Configuration standard BM632w', version: '1.0.1', priority: 100, policy: PolicyType.ONCE, enabled: true, tags: [], criteria: { productClass: 'BM632w' }, inventory: [], desiredParameters: [ { name: 'Device.Param.B', value: 'hello', type: 'string' }, { name: 'Device.Param.A', value: 42, type: 'int' }, ], firmwareTarget: { productClass: 'BM632w', targetVersion: '2.1.0' }, createdAt: new Date(), updatedAt: new Date(), ...overrides, } } describe('computeContentHash', () => { it('returns a 12-character hex string', () => { const hash = computeContentHash(makeConfig()) expect(hash).toMatch(/^[a-f0-9]{12}$/) }) it('is deterministic for the same config', () => { const config = makeConfig() expect(computeContentHash(config)).toBe(computeContentHash(config)) }) it('sorts desiredParameters by name for consistency', () => { const config1 = makeConfig({ desiredParameters: [ { name: 'B.Param', value: 1, type: 'int' }, { name: 'A.Param', value: 2, type: 'int' }, ], }) const config2 = makeConfig({ desiredParameters: [ { name: 'A.Param', value: 2, type: 'int' }, { name: 'B.Param', value: 1, type: 'int' }, ], }) expect(computeContentHash(config1)).toBe(computeContentHash(config2)) }) it('changes when desiredParameters change', () => { const config1 = makeConfig({ desiredParameters: [{ name: 'A.Param', value: 1, type: 'int' }], }) const config2 = makeConfig({ desiredParameters: [{ name: 'A.Param', value: 2, type: 'int' }], }) expect(computeContentHash(config1)).not.toBe(computeContentHash(config2)) }) it('changes when firmwareTarget changes', () => { const config1 = makeConfig({ firmwareTarget: { productClass: 'BM632w', targetVersion: '2.0.0' }, }) const config2 = makeConfig({ firmwareTarget: { productClass: 'BM632w', targetVersion: '3.0.0' }, }) expect(computeContentHash(config1)).not.toBe(computeContentHash(config2)) }) }) describe('buildProvisioningCode', () => { it('returns the correct format', () => { const config = makeConfig() const code = buildProvisioningCode(config) expect(code).toMatch(/^BM632w-standard\(v1\.0\.1\)-[a-f0-9]{12}$/) }) it('matches the regex pattern from the spec', () => { const config = makeConfig() const code = buildProvisioningCode(config) expect(code).toMatch(/^(.+)\(v(.+)\)-([a-f0-9]{12})$/) }) }) describe('parseProvisioningCode', () => { it('parses a valid provisioning code', () => { const result = parseProvisioningCode('BM632w-standard(v1.0.1)-a1b2c3d4e5f6') expect(result).toEqual({ identifier: 'BM632w-standard', version: '1.0.1', hash: 'a1b2c3d4e5f6', }) }) it('returns null for invalid format', () => { expect(parseProvisioningCode('invalid')).toBeNull() expect(parseProvisioningCode('')).toBeNull() expect(parseProvisioningCode('id(v1.0)-short')).toBeNull() // hash too short }) it('round-trips with buildProvisioningCode', () => { const config = makeConfig() const code = buildProvisioningCode(config) const parsed = parseProvisioningCode(code) expect(parsed).not.toBeNull() expect(parsed!.identifier).toBe('BM632w-standard') expect(parsed!.version).toBe('1.0.1') expect(parsed!.hash).toMatch(/^[a-f0-9]{12}$/) }) })