Files
growqr-backend/scripts/redis-gating.test.ts
-Puter ac7bdca912 feat(backend): REST-authoritative qscore pipeline with Redis opt-in
- Gate both Redis ingestion paths (canonical + legacy) behind separate
  default-off flags (GROW_EVENTS_REDIS_ENABLED, LEGACY_SERVICE_REDIS_ENABLED).
  The redis module is never imported when both flags are off.
- Sever legacy URL cascade: interview/roleplay/resume/courses Redis URLs
  resolve ONLY from their own explicit env vars, never from REDIS_URL or
  GROW_EVENTS_REDIS_URL.
- Export resolveRedisConfig() pure resolver for testability.
- Fix onboarding signal ID from 'onboarding.completed_baseline' to
  'onboarding.completed' (registry-valid in all v2 formula JSONs).
- Ensure interview/roleplay completed events emit at least a single
  completion signal when session_count/scenario_count is absent (default 1).
- Extract computeStreakFromDays() into streak-utils.ts (pure, no DB dep)
  with defensive dedup so duplicate days never inflate streaks.
- Add focused tests: redis-gating, signal-registry membership, count
  fallback, streak policy (current/longest/duplicate/gap/recovery/7-day),
  service-ingest projector behavior.
- typecheck: 80 pre-existing errors unchanged, 0 new.
2026-07-10 20:12:13 +05:30

113 lines
3.6 KiB
TypeScript

import assert from "node:assert/strict";
import { resolveRedisConfig } from "../src/config.js";
/**
* Test: Redis ingestion consumers are default-off and do not load the Redis
* module unless an explicit opt-in flag is set. Legacy URLs must not inherit
* from the generic REDIS_URL.
*
* Uses the pure resolveRedisConfig resolver with synthetic env objects — no
* module-cache hacks needed.
*/
// ── 1. Default-off: no Redis flags set ──────────────────────────────────────
{
const rc = resolveRedisConfig({});
assert.equal(
rc.growEventsRedisEnabled,
false,
"GROW_EVENTS_REDIS_ENABLED must default to false",
);
assert.equal(
rc.legacyServiceRedisEnabled,
false,
"LEGACY_SERVICE_REDIS_ENABLED must default to false",
);
}
// ── 2. Legacy URLs must NOT inherit from generic REDIS_URL ──────────────────
{
// Set ONLY the generic REDIS_URL — legacy URLs must not pick it up.
const rc = resolveRedisConfig({ REDIS_URL: "redis://legacy-generic:6379" });
assert.equal(
rc.interviewRedisUrl,
"",
"interviewRedisUrl must NOT inherit from generic REDIS_URL",
);
assert.equal(
rc.roleplayRedisUrl,
"",
"roleplayRedisUrl must NOT inherit from generic REDIS_URL",
);
assert.equal(
rc.resumeRedisUrl,
"",
"resumeRedisUrl must NOT inherit from generic REDIS_URL",
);
assert.equal(
rc.coursesRedisUrl,
"",
"coursesRedisUrl must NOT inherit from generic REDIS_URL",
);
}
// ── 3. Legacy URLs must NOT inherit from GROW_EVENTS_REDIS_URL ───────────────
{
const rc = resolveRedisConfig({ GROW_EVENTS_REDIS_URL: "redis://canonical:6379" });
assert.equal(
rc.interviewRedisUrl,
"",
"interviewRedisUrl must NOT inherit from GROW_EVENTS_REDIS_URL",
);
assert.equal(rc.growEventsRedisUrl, "redis://canonical:6379", "canonical URL resolves from its own var");
}
// ── 4. Explicit opt-in flags are respected ──────────────────────────────────
{
const rc = resolveRedisConfig({ GROW_EVENTS_REDIS_ENABLED: "true" });
assert.equal(
rc.growEventsRedisEnabled,
true,
"GROW_EVENTS_REDIS_ENABLED=true must be respected",
);
assert.equal(
rc.legacyServiceRedisEnabled,
false,
"legacy must stay off when only canonical flag is set",
);
}
// ── 5. Legacy opt-in flag works independently ───────────────────────────────
{
const rc = resolveRedisConfig({
LEGACY_SERVICE_REDIS_ENABLED: "true",
INTERVIEW_REDIS_URL: "redis://interview:6379",
});
assert.equal(rc.legacyServiceRedisEnabled, true, "LEGACY_SERVICE_REDIS_ENABLED=true must be respected");
assert.equal(rc.growEventsRedisEnabled, false, "canonical must stay off when only legacy flag is set");
assert.equal(rc.interviewRedisUrl, "redis://interview:6379", "explicit interview URL resolves");
}
// ── 6. Explicit legacy URLs resolve from their own vars ──────────────────────
{
const rc = resolveRedisConfig({
INTERVIEW_REDIS_URL: "redis://i:6379",
ROLEPLAY_REDIS_URL: "redis://r:6379",
RESUME_REDIS_URL: "redis://re:6379",
COURSES_REDIS_URL: "redis://c:6379",
});
assert.equal(rc.interviewRedisUrl, "redis://i:6379");
assert.equal(rc.roleplayRedisUrl, "redis://r:6379");
assert.equal(rc.resumeRedisUrl, "redis://re:6379");
assert.equal(rc.coursesRedisUrl, "redis://c:6379");
}
console.log("redis-gating tests passed");
process.exit(0);