feat: ATM10-Statistikportal
Statistikportal für den All-the-Mods-10-Server (Minecraft 1.21.1, NeoForge 21.1): - mod/: serverseitige NeoForge-Mod atmstats – Ereignisse, Live-Zähler, Servergesundheit, FTB-Dateien; Outbox auf der Festplatte, signierter gzip-Versand per HTTPS mit Backoff - backend/: Fastify + PostgreSQL – idempotenter Ingest, Projektionen, Parser für SNBT, FTB Quests/Teams, world/stats, world/advancements und latest.log; Ranglisten, Snapshots, Retention, öffentliche API, SSE-Live-Updates, Admin mit argon2/CSRF, SFTP-Worker - web/: Next.js-Dashboard – Übersicht, Ranglisten, Spielerprofile, ATM10, Server, Datenquellen, Verwaltung - shared/: Protokoll v1 und API-Typen; tools/: Simulator, Log-Anonymisierer, RCON-Client - deploy/: Docker Compose hinter Traefik, Backup; docs/: Architektur, Datenquellen, Betrieb Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
56d2078e6a
commit
e66f89f49e
214 files changed
+27077
No files matched your search
@@ -0,0 +1,34 @@
|
||||
import { readdirSync, readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { atmEventSchema, deterministicId, ingestBatchSchema } from '../src/index.ts';
|
||||
|
||||
const dir = join(import.meta.dirname, '..', 'fixtures', 'protocol');
|
||||
|
||||
describe('protocol v1', () => {
|
||||
it('every fixture validates', () => {
|
||||
const files = readdirSync(dir).filter((f) => f.endsWith('.json'));
|
||||
expect(files.length).toBe(11);
|
||||
for (const f of files) {
|
||||
const parsed = atmEventSchema.parse(JSON.parse(readFileSync(join(dir, f), 'utf8')));
|
||||
expect(parsed.type).toBe(f.replace('.json', ''));
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects unknown type', () => {
|
||||
const r = atmEventSchema.safeParse({ id: '0199d6a0-0000-7000-8000-000000000001', type: 'nope', ts: 1 });
|
||||
expect(r.success).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects batch with 501 events', () => {
|
||||
const ev = JSON.parse(readFileSync(join(dir, 'server_stop.json'), 'utf8'));
|
||||
const r = ingestBatchSchema.safeParse({ protocol: 1, events: Array.from({ length: 501 }, () => ev) });
|
||||
expect(r.success).toBe(false);
|
||||
});
|
||||
|
||||
it('deterministicId is stable', () => {
|
||||
expect(deterministicId('a')).toBe(deterministicId('a'));
|
||||
expect(deterministicId('a')).not.toBe(deterministicId('b'));
|
||||
expect(deterministicId('a')).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user