harden state durability and disable destructive snapshot sync
This commit is contained in:
@@ -31,6 +31,7 @@ function withTempEnv(patch, run) {
|
||||
|
||||
test("config uses defaults when env is missing", () => {
|
||||
withTempEnv({
|
||||
NODE_ENV: "",
|
||||
PORT: "",
|
||||
LOG_LEVEL: "",
|
||||
APP_BASE_URL: "",
|
||||
@@ -42,8 +43,10 @@ test("config uses defaults when env is missing", () => {
|
||||
MINIO_SIGNED_URL_TTL_SEC: "",
|
||||
MINIO_USE_SSL: "",
|
||||
WEBHOOK_RPM: "",
|
||||
ALLOW_IN_MEMORY_STATE_FALLBACK: "",
|
||||
}, () => {
|
||||
const { config } = require("../src/config");
|
||||
assert.equal(config.nodeEnv, "development");
|
||||
assert.equal(config.port, 3000);
|
||||
assert.equal(config.logLevel, "info");
|
||||
assert.equal(config.appBaseUrl, "http://localhost:3000");
|
||||
@@ -55,6 +58,7 @@ test("config uses defaults when env is missing", () => {
|
||||
assert.equal(config.minioSignedUrlTtlSec, 3600);
|
||||
assert.equal(config.minioUseSSL, true);
|
||||
assert.equal(config.rateLimits.webhookPerMinute, 120);
|
||||
assert.equal(config.allowInMemoryStateFallback, true);
|
||||
assert.equal(config.abuse.maxJobsPerUserPerDay, 0);
|
||||
assert.equal(config.abuse.cooldownSec, 0);
|
||||
assert.deepEqual(config.abuse.denyUserIds, []);
|
||||
@@ -63,6 +67,7 @@ test("config uses defaults when env is missing", () => {
|
||||
|
||||
test("config reads convex/qwen/minio overrides", () => {
|
||||
withTempEnv({
|
||||
NODE_ENV: "production",
|
||||
PORT: "8080",
|
||||
LOG_LEVEL: "debug",
|
||||
APP_BASE_URL: "https://xartaudio.app",
|
||||
@@ -86,8 +91,10 @@ test("config reads convex/qwen/minio overrides", () => {
|
||||
ABUSE_MAX_JOBS_PER_USER_PER_DAY: "5",
|
||||
ABUSE_COOLDOWN_SEC: "120",
|
||||
ABUSE_DENY_USER_IDS: "u1,u2",
|
||||
ALLOW_IN_MEMORY_STATE_FALLBACK: "",
|
||||
}, () => {
|
||||
const { config } = require("../src/config");
|
||||
assert.equal(config.nodeEnv, "production");
|
||||
assert.equal(config.port, 8080);
|
||||
assert.equal(config.logLevel, "debug");
|
||||
assert.equal(config.appBaseUrl, "https://xartaudio.app");
|
||||
@@ -109,6 +116,17 @@ test("config reads convex/qwen/minio overrides", () => {
|
||||
assert.equal(config.abuse.maxJobsPerUserPerDay, 5);
|
||||
assert.equal(config.abuse.cooldownSec, 120);
|
||||
assert.deepEqual(config.abuse.denyUserIds, ["u1", "u2"]);
|
||||
assert.equal(config.allowInMemoryStateFallback, false);
|
||||
});
|
||||
});
|
||||
|
||||
test("allow in-memory fallback can be explicitly enabled in production", () => {
|
||||
withTempEnv({
|
||||
NODE_ENV: "production",
|
||||
ALLOW_IN_MEMORY_STATE_FALLBACK: "true",
|
||||
}, () => {
|
||||
const { config } = require("../src/config");
|
||||
assert.equal(config.allowInMemoryStateFallback, true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const {
|
||||
|
||||
function createRuntimeConfig() {
|
||||
return {
|
||||
nodeEnv: "test",
|
||||
port: 3000,
|
||||
logLevel: "info",
|
||||
appBaseUrl: "http://localhost:3000",
|
||||
@@ -60,6 +61,7 @@ function createRuntimeConfig() {
|
||||
stepCredits: 1,
|
||||
maxCharsPerArticle: 120000,
|
||||
},
|
||||
allowInMemoryStateFallback: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -153,3 +155,39 @@ test("createRuntime falls back to in-memory state when initial load fails", asyn
|
||||
assert.equal(response.status, 303);
|
||||
await runtime.persister.flush();
|
||||
});
|
||||
|
||||
test("createRuntime fails startup when fallback is disabled", async () => {
|
||||
const runtimeConfig = createRuntimeConfig();
|
||||
runtimeConfig.allowInMemoryStateFallback = false;
|
||||
|
||||
await assert.rejects(
|
||||
createRuntime({
|
||||
runtimeConfig,
|
||||
logger: { info() {}, warn() {}, error() {} },
|
||||
stateStore: {
|
||||
async load() {
|
||||
throw new Error("state_load_failed");
|
||||
},
|
||||
async save() {},
|
||||
},
|
||||
}),
|
||||
/state_store_unavailable_without_fallback/,
|
||||
);
|
||||
});
|
||||
|
||||
test("createMutationPersister surfaces save errors", async () => {
|
||||
const persister = createMutationPersister({
|
||||
stateStore: {
|
||||
async save() {
|
||||
throw new Error("persist_failed");
|
||||
},
|
||||
},
|
||||
logger: { error() {} },
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
persister.enqueue({}),
|
||||
/persist_failed/,
|
||||
);
|
||||
assert.equal(persister.getLastError()?.message, "persist_failed");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user