fix(roleplay): hydrate qscore with canonical identity
This commit is contained in:
@@ -0,0 +1,74 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { buildPersonalizedRoleplayConfigurePayload } from "../src/routes/services.js";
|
||||||
|
import { toQscoreUserId } from "../src/services/qscore-proxy.js";
|
||||||
|
|
||||||
|
const clerkUserId = "user_3GTcs6PBFcUrXUP6bH6U8dcwXBq";
|
||||||
|
const originalFetch = globalThis.fetch;
|
||||||
|
const requests: Request[] = [];
|
||||||
|
|
||||||
|
globalThis.fetch = (async (input, init) => {
|
||||||
|
const request = new Request(input, init);
|
||||||
|
requests.push(request);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
org_id: "growqr",
|
||||||
|
user_id: toQscoreUserId(clerkUserId),
|
||||||
|
rq_score: 61,
|
||||||
|
iq_score: 60,
|
||||||
|
eq_score: 62,
|
||||||
|
sq_score: 61,
|
||||||
|
profession: "General",
|
||||||
|
formula_version: "v1",
|
||||||
|
formula_id: "default",
|
||||||
|
calculated_at: "2026-07-14T00:00:00Z",
|
||||||
|
quotients: {},
|
||||||
|
breakdown: {},
|
||||||
|
ledger_seq_from: null,
|
||||||
|
ledger_seq_to: null,
|
||||||
|
}),
|
||||||
|
{ status: 200, headers: { "content-type": "application/json" } },
|
||||||
|
);
|
||||||
|
}) as typeof fetch;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = await buildPersonalizedRoleplayConfigurePayload(
|
||||||
|
new Request("http://backend.test/roleplay/preview"),
|
||||||
|
{
|
||||||
|
scenario_type: "salary-negotiation",
|
||||||
|
metadata: { difficulty: "medium" },
|
||||||
|
},
|
||||||
|
clerkUserId,
|
||||||
|
{
|
||||||
|
resolveUserContext: async () => ({ clerk_id: clerkUserId }),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(requests.length, 1);
|
||||||
|
const qscoreUrl = new URL(requests[0]!.url);
|
||||||
|
assert.equal(qscoreUrl.pathname, `/v1/qscore/${toQscoreUserId(clerkUserId)}`);
|
||||||
|
assert.equal(qscoreUrl.searchParams.get("org_id"), "growqr");
|
||||||
|
assert.equal((payload.qscore as Record<string, unknown>).rq_score, 61);
|
||||||
|
assert.equal(payload.user_id, clerkUserId);
|
||||||
|
|
||||||
|
requests.length = 0;
|
||||||
|
const suppliedPayload = await buildPersonalizedRoleplayConfigurePayload(
|
||||||
|
new Request("http://backend.test/roleplay/preview"),
|
||||||
|
{
|
||||||
|
qscore: { rq_score: 74, source: "caller" },
|
||||||
|
metadata: {},
|
||||||
|
},
|
||||||
|
clerkUserId,
|
||||||
|
{
|
||||||
|
resolveUserContext: async () => ({
|
||||||
|
clerk_id: clerkUserId,
|
||||||
|
qscore: { rq_score: 68, source: "context" },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert.equal(requests.length, 0);
|
||||||
|
assert.deepEqual(suppliedPayload.qscore, { rq_score: 74, source: "caller" });
|
||||||
|
|
||||||
|
console.log("roleplay QScore identity: all assertions passed");
|
||||||
|
} finally {
|
||||||
|
globalThis.fetch = originalFetch;
|
||||||
|
}
|
||||||
+20
-7
@@ -668,9 +668,17 @@ async function buildPersonalizedConfigurePayload(req: Request, body: JsonObject,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildPersonalizedRoleplayConfigurePayload(req: Request, body: JsonObject, userId: string): Promise<JsonObject> {
|
export async function buildPersonalizedRoleplayConfigurePayload(
|
||||||
|
req: Request,
|
||||||
|
body: JsonObject,
|
||||||
|
userId: string,
|
||||||
|
dependencies: {
|
||||||
|
resolveUserContext?: typeof resolveGrowUserContext;
|
||||||
|
loadQscore?: typeof getQscoreFromService;
|
||||||
|
} = {},
|
||||||
|
): Promise<JsonObject> {
|
||||||
const { mission: _mission, ...rest } = body;
|
const { mission: _mission, ...rest } = body;
|
||||||
const userContext = await resolveGrowUserContext(req, userId).catch((err) => {
|
const userContext = await (dependencies.resolveUserContext ?? resolveGrowUserContext)(req, userId).catch((err) => {
|
||||||
log.warn({ err, userId }, "failed to resolve Grow user context for roleplay configure");
|
log.warn({ err, userId }, "failed to resolve Grow user context for roleplay configure");
|
||||||
return {} as Record<string, unknown>;
|
return {} as Record<string, unknown>;
|
||||||
});
|
});
|
||||||
@@ -692,14 +700,19 @@ async function buildPersonalizedRoleplayConfigurePayload(req: Request, body: Jso
|
|||||||
metadata.context_notes = getString(incomingMetadata.context_notes) ?? candidateProfile;
|
metadata.context_notes = getString(incomingMetadata.context_notes) ?? candidateProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const orgId = String(rest.org_id ?? "growqr");
|
||||||
|
const suppliedQscore = isRecord(rest.qscore)
|
||||||
|
? rest.qscore
|
||||||
|
: isRecord(userContext.qscore)
|
||||||
|
? userContext.qscore
|
||||||
|
: undefined;
|
||||||
|
const qscore = suppliedQscore ?? await (dependencies.loadQscore ?? getQscoreFromService)(userId, orgId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...rest,
|
...rest,
|
||||||
user_id: String(rest.user_id ?? userId),
|
user_id: String(rest.user_id ?? userId),
|
||||||
org_id: String(rest.org_id ?? "growqr"),
|
org_id: orgId,
|
||||||
metadata,
|
...(qscore ? { qscore } : {}),
|
||||||
...((rest.qscore as JsonObject | undefined) ?? (isRecord(userContext.qscore) ? userContext.qscore : undefined)
|
|
||||||
? { qscore: (rest.qscore as JsonObject | undefined) ?? userContext.qscore }
|
|
||||||
: {}),
|
|
||||||
user_context: userContext,
|
user_context: userContext,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user