-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy patharchive-forwarder-strategy-request.test.ts
More file actions
135 lines (127 loc) · 4.14 KB
/
Copy patharchive-forwarder-strategy-request.test.ts
File metadata and controls
135 lines (127 loc) · 4.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { describe, expect, test } from "bun:test";
import { handleArchiveRequest } from "../services/archive-forwarder/request";
import { StrategyArchiveSpool } from "../services/archive-forwarder/strategy-spool";
import {
ArchiveForwarderTelemetry,
type ArchiveMetricsRecorder,
} from "../services/archive-forwarder/telemetry";
import fixture from "./fixtures/archive_forwarder_envelope.json";
import makerOrchestratorFixture from "./fixtures/maker_orchestrator_archive_envelope.json";
const noopRecorder: ArchiveMetricsRecorder = {
recordCounter: () => {},
setObservableGauge: () => {},
};
function post(body: unknown): Request {
return new Request("http://localhost/archive", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(body),
});
}
describe("strategy durable HTTP admission", () => {
test("returns 202 after SQLite ownership without waiting for ClickHouse", async () => {
const spool = new StrategyArchiveSpool({ path: ":memory:" });
let insertCalled = false;
const response = await handleArchiveRequest(post(fixture), {
inserter: async () => {
insertCalled = true;
throw new Error("ClickHouse is down");
},
spool,
telemetry: new ArchiveForwarderTelemetry(noopRecorder),
});
expect(response.status).toBe(202);
expect(insertCalled).toBe(false);
expect(spool.stats()).toMatchObject({
queuedBatches: 1,
queuedWork: fixture.rows.length,
});
spool.close();
});
test("durably admits Maker orchestrator rows without waiting for ClickHouse", async () => {
const spool = new StrategyArchiveSpool({ path: ":memory:" });
let insertCalled = false;
const response = await handleArchiveRequest(
post(makerOrchestratorFixture),
{
inserter: async () => {
insertCalled = true;
throw new Error("ClickHouse is down");
},
spool,
telemetry: new ArchiveForwarderTelemetry(noopRecorder),
},
);
expect(response.status).toBe(202);
expect(insertCalled).toBe(false);
expect(spool.stats()).toMatchObject({
queuedBatches: 1,
queuedWork: makerOrchestratorFixture.rows.length,
});
spool.close();
});
test("keeps broker traffic on direct synchronous insertion", async () => {
const spool = new StrategyArchiveSpool({ path: ":memory:" });
let insertCalled = false;
const response = await handleArchiveRequest(
post({
source: "broker_read",
deployment_id: "broker-a",
rows: [
{
table: "market_data.cex_trades",
row: { source: "broker_read", trade_id: "trade-a" },
},
],
}),
{
inserter: async () => {
insertCalled = true;
},
spool,
telemetry: new ArchiveForwarderTelemetry(noopRecorder),
},
);
expect(response.status).toBe(200);
expect(insertCalled).toBe(true);
expect(spool.stats().queuedBatches).toBe(0);
spool.close();
});
test("rejects strategy source/table/version failures before admission", async () => {
const spool = new StrategyArchiveSpool({ path: ":memory:" });
const invalid = structuredClone(fixture);
invalid.rows[0].row.schema_version = "99";
const response = await handleArchiveRequest(post(invalid), {
inserter: async () => {},
spool,
telemetry: new ArchiveForwarderTelemetry(noopRecorder),
});
expect(response.status).toBe(400);
expect(spool.stats().queuedBatches).toBe(0);
spool.close();
});
test("returns 429 when the spool cannot reserve fixed capacity", async () => {
const probe = new StrategyArchiveSpool({ path: ":memory:" });
const required = probe.accountedBytes(fixture);
probe.close();
const spool = new StrategyArchiveSpool({
path: ":memory:",
limits: { maxBytes: required - 1 },
});
const response = await handleArchiveRequest(post(fixture), {
inserter: async () => {},
spool,
telemetry: new ArchiveForwarderTelemetry(noopRecorder),
});
expect(response.status).toBe(429);
expect(spool.stats().queuedBatches).toBe(0);
spool.close();
});
test("returns 503 when no durable strategy spool is available", async () => {
const response = await handleArchiveRequest(post(fixture), {
inserter: async () => {},
telemetry: new ArchiveForwarderTelemetry(noopRecorder),
});
expect(response.status).toBe(503);
});
});