import { test } from '@japa/runner' import db from '@adonisjs/lucid/services/db' import testUtils from '@adonisjs/core/services/test_utils' import coordinatorConfig from '#config/coordinator' import { grant, targets, NntpShare } from '#services/nntp_shares' test.group('NNTP shares', (group) => { group.each.setup(() => testUtils.db().withGlobalTransaction()) test('targets: minimums first, then by priority what each wants', ({ assert }) => { const coordinator = { name: 'coordinator', priority: 2, min: 8, want: 38, open: 0 } const prober = { name: 'prober', priority: 1, min: 2, want: 40, open: 0 } // History running: the coordinator all it wants, the prober the rest. assert.deepEqual(Object.fromEntries(targets(45, [prober, coordinator])), { coordinator: 38, prober: 7 }) // History paused: the coordinator its minimum (or what it uses), the prober the rest. assert.deepEqual(Object.fromEntries(targets(45, [prober, { ...coordinator, want: 8 }])), { coordinator: 8, prober: 37 }) // Wanting more than its minimum doesn't take another's minimum. assert.deepEqual(Object.fromEntries(targets(45, [prober, { ...coordinator, want: 60 }])), { coordinator: 43, prober: 2 }) }) test('grant: growing only into connections the others closed or gave up', ({ assert }) => { const prober = { name: 'prober', priority: 1, min: 2, want: 40, open: 37, granted: 37 } const coordinator = { name: 'coordinator', priority: 2, min: 8, want: 38, open: 8 } // History came back: the prober still holds 37, the coordinator keeps its 8 for now. assert.equal(grant(45, coordinator, [prober]), 8) // The prober syncs: its target is 7, its grant goes down at once. assert.equal(grant(45, { ...prober, open: 37 }, [{ ...coordinator, granted: 8 }]), 7) // Its connections not closed yet: still held. assert.equal(grant(45, coordinator, [{ ...prober, granted: 7, open: 30 }]), 15) // Closed: the coordinator gets all it wants. assert.equal(grant(45, coordinator, [{ ...prober, granted: 7, open: 7 }]), 38) }) test('sync: each process its row; a process gone (no heartbeat) holds nothing', async ({ assert }) => { const total = coordinatorConfig.nntpShares.total const coordinator = new NntpShare('coordinator', 2, 8, 38) const prober = new NntpShare('prober', 1, 2, total) assert.equal(await coordinator.sync(8, 0), 8) assert.equal(await prober.sync(total, 0), total - 8) // The coordinator wants all: the prober holds its grant until it has synced and closed them. assert.equal(await coordinator.sync(38, 8), 8) assert.equal(await prober.sync(total, total - 8), total - 38) assert.equal(await coordinator.sync(38, 8), 8) assert.equal(await prober.sync(total, total - 38), total - 38) assert.equal(await coordinator.sync(38, 8), 38) // The prober's process died: after a minute without a sync its share is free. await db.from('nntp_shares').where('name', 'prober').update({ open: total, granted: total, heartbeat_at: new Date(Date.now() - 120_000) }) assert.equal(await coordinator.sync(38, 38), 38) await coordinator.leave() assert.equal(await prober.sync(total, 0), total) }) })