| import { describe, it, beforeEach, afterEach } from 'node:test'; |
| import assert from 'node:assert/strict'; |
| import { mkdirSync, mkdtempSync, rmSync, writeFileSync, readFileSync, existsSync } from 'fs'; |
| import { join } from 'path'; |
| import { tmpdir } from 'os'; |
| import { migrateReplicaAccountsTo } from '../src/auth.js'; |
|
|
| |
| |
| |
| |
| |
|
|
| const silentLogger = { warn() {}, error() {}, info() {} }; |
|
|
| describe('migrateReplicaAccountsTo (issue #67)', () => { |
| let tmp; |
| let accountsFile; |
|
|
| beforeEach(() => { |
| tmp = mkdtempSync(join(tmpdir(), 'wfapi-mig-')); |
| accountsFile = join(tmp, 'accounts.json'); |
| }); |
|
|
| afterEach(() => { |
| try { rmSync(tmp, { recursive: true, force: true }); } catch {} |
| }); |
|
|
| it('skips when no replica-* subdir exists', () => { |
| const r = migrateReplicaAccountsTo({ sharedDir: tmp, accountsFile, logger: silentLogger }); |
| assert.equal(r.migrated, 0); |
| assert.equal(r.skipped, true); |
| assert.equal(existsSync(accountsFile), false); |
| }); |
|
|
| it('skips when shared accounts.json already exists', () => { |
| mkdirSync(join(tmp, 'replica-h1')); |
| writeFileSync(join(tmp, 'replica-h1', 'accounts.json'), JSON.stringify([ |
| { apiKey: 'k1', email: 'a@b.com' }, |
| ])); |
| writeFileSync(accountsFile, '[]'); |
| const r = migrateReplicaAccountsTo({ sharedDir: tmp, accountsFile, logger: silentLogger }); |
| assert.equal(r.migrated, 0); |
| assert.equal(r.skipped, true); |
| assert.equal(readFileSync(accountsFile, 'utf-8'), '[]'); |
| }); |
|
|
| it('migrates accounts from a single replica-*/accounts.json', () => { |
| mkdirSync(join(tmp, 'replica-h1')); |
| writeFileSync(join(tmp, 'replica-h1', 'accounts.json'), JSON.stringify([ |
| { apiKey: 'k1', email: 'a@b.com', method: 'api_key' }, |
| { apiKey: 'k2', email: 'b@b.com', method: 'api_key' }, |
| ])); |
|
|
| const r = migrateReplicaAccountsTo({ sharedDir: tmp, accountsFile, logger: silentLogger }); |
| assert.equal(r.migrated, 2); |
| assert.equal(r.scanned, 1); |
| assert.equal(r.skipped, false); |
| const out = JSON.parse(readFileSync(accountsFile, 'utf-8')); |
| assert.equal(out.length, 2); |
| assert.deepEqual(out.map(a => a.apiKey).sort(), ['k1', 'k2']); |
| }); |
|
|
| it('unions multiple replica-* subdirs and dedupes by apiKey', () => { |
| for (const host of ['h1', 'h2', 'h3']) mkdirSync(join(tmp, `replica-${host}`)); |
| writeFileSync(join(tmp, 'replica-h1', 'accounts.json'), JSON.stringify([ |
| { apiKey: 'k1', email: 'a@b.com' }, |
| { apiKey: 'k2', email: 'b@b.com' }, |
| ])); |
| writeFileSync(join(tmp, 'replica-h2', 'accounts.json'), JSON.stringify([ |
| { apiKey: 'k2', email: 'b-second@b.com' }, |
| { apiKey: 'k3', email: 'c@b.com' }, |
| ])); |
| writeFileSync(join(tmp, 'replica-h3', 'accounts.json'), JSON.stringify([ |
| { apiKey: 'k4', email: 'd@b.com' }, |
| ])); |
|
|
| const r = migrateReplicaAccountsTo({ sharedDir: tmp, accountsFile, logger: silentLogger }); |
| assert.equal(r.migrated, 4); |
| assert.equal(r.scanned, 3); |
| const out = JSON.parse(readFileSync(accountsFile, 'utf-8')); |
| assert.equal(out.length, 4); |
| assert.deepEqual(out.map(a => a.apiKey).sort(), ['k1', 'k2', 'k3', 'k4']); |
| |
| assert.equal(out.find(a => a.apiKey === 'k2').email, 'b@b.com'); |
| }); |
|
|
| it('tolerates a corrupt replica-*/accounts.json without aborting other replicas', () => { |
| mkdirSync(join(tmp, 'replica-bad')); |
| mkdirSync(join(tmp, 'replica-good')); |
| writeFileSync(join(tmp, 'replica-bad', 'accounts.json'), '{not json'); |
| writeFileSync(join(tmp, 'replica-good', 'accounts.json'), JSON.stringify([ |
| { apiKey: 'k1', email: 'a@b.com' }, |
| ])); |
|
|
| const r = migrateReplicaAccountsTo({ sharedDir: tmp, accountsFile, logger: silentLogger }); |
| assert.equal(r.migrated, 1); |
| assert.equal(r.scanned, 2); |
| const out = JSON.parse(readFileSync(accountsFile, 'utf-8')); |
| assert.equal(out.length, 1); |
| assert.equal(out[0].apiKey, 'k1'); |
| }); |
|
|
| it('skips replica-* subdirs that have no accounts.json', () => { |
| mkdirSync(join(tmp, 'replica-empty')); |
| mkdirSync(join(tmp, 'replica-with-data')); |
| writeFileSync(join(tmp, 'replica-with-data', 'accounts.json'), JSON.stringify([ |
| { apiKey: 'k1', email: 'a@b.com' }, |
| ])); |
|
|
| const r = migrateReplicaAccountsTo({ sharedDir: tmp, accountsFile, logger: silentLogger }); |
| assert.equal(r.migrated, 1); |
| assert.equal(r.scanned, 1); |
| }); |
|
|
| it('does nothing when sharedDir does not exist', () => { |
| const r = migrateReplicaAccountsTo({ |
| sharedDir: join(tmp, 'does-not-exist'), |
| accountsFile, |
| logger: silentLogger, |
| }); |
| assert.equal(r.migrated, 0); |
| assert.equal(r.skipped, true); |
| }); |
| }); |
|
|