75 lines
3.4 KiB
TypeScript
75 lines
3.4 KiB
TypeScript
/**
|
|
* Tests for Anthropic billing header fingerprint stability (#1638).
|
|
*
|
|
* Validates that the billing header fingerprint is stable across different
|
|
* messages within the same day, preventing prompt-cache prefix invalidation.
|
|
*/
|
|
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { createHash } from "node:crypto";
|
|
|
|
// Replicate the stabilized fingerprint logic from base.ts
|
|
function computeStableFingerprint(ccVersion: string): string {
|
|
const dayStamp = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
|
|
return createHash("sha256").update(`${dayStamp}${ccVersion}`).digest("hex").slice(0, 3);
|
|
}
|
|
|
|
// The old implementation for comparison
|
|
function computeOldFingerprint(firstUserMessageText: string, version: string): string {
|
|
const FINGERPRINT_SALT = "59cf53e54c78";
|
|
const indices = [4, 7, 20];
|
|
const chars = indices.map((i) => firstUserMessageText[i] || "0").join("");
|
|
const input = `${FINGERPRINT_SALT}${chars}${version}`;
|
|
return createHash("sha256").update(input).digest("hex").slice(0, 3);
|
|
}
|
|
|
|
describe("Anthropic billing header fingerprint (#1638)", () => {
|
|
const ccVersion = "2.1.137";
|
|
|
|
it("should produce the same fingerprint for different messages (stable)", () => {
|
|
const fp1 = computeStableFingerprint(ccVersion);
|
|
const fp2 = computeStableFingerprint(ccVersion);
|
|
assert.equal(fp1, fp2, "Same-day fingerprints should be identical");
|
|
});
|
|
|
|
it("should produce a 3-character hex fingerprint", () => {
|
|
const fp = computeStableFingerprint(ccVersion);
|
|
assert.equal(fp.length, 3, "Fingerprint should be 3 chars");
|
|
assert.ok(/^[a-f0-9]{3}$/.test(fp), `Fingerprint '${fp}' should be lowercase hex`);
|
|
});
|
|
|
|
it("old implementation produces DIFFERENT fingerprints for different messages", () => {
|
|
const msg1 = "Hello, how can I help you with your code?";
|
|
const msg2 = "Please fix the bug in my application";
|
|
const fp1 = computeOldFingerprint(msg1, ccVersion);
|
|
const fp2 = computeOldFingerprint(msg2, ccVersion);
|
|
assert.notEqual(fp1, fp2, "Old method should differ per message — this was the bug");
|
|
});
|
|
|
|
it("new implementation produces SAME fingerprint regardless of message content", () => {
|
|
// The new implementation doesn't use message content at all
|
|
const fp1 = computeStableFingerprint(ccVersion);
|
|
const fp2 = computeStableFingerprint(ccVersion);
|
|
const fp3 = computeStableFingerprint(ccVersion);
|
|
assert.equal(fp1, fp2);
|
|
assert.equal(fp2, fp3);
|
|
});
|
|
|
|
it("billing header line should be deterministic within the same day", () => {
|
|
const fp = computeStableFingerprint(ccVersion);
|
|
const billingLine1 = `x-anthropic-billing-header: cc_version=${ccVersion}.${fp}; cc_entrypoint=cli; cch=00000;`;
|
|
const billingLine2 = `x-anthropic-billing-header: cc_version=${ccVersion}.${fp}; cc_entrypoint=cli; cch=00000;`;
|
|
assert.equal(billingLine1, billingLine2, "Billing lines should be byte-identical");
|
|
});
|
|
|
|
it("should produce different fingerprints for different days", () => {
|
|
// Simulate different days by computing manually
|
|
const day1 = "2026-04-27";
|
|
const day2 = "2026-04-28";
|
|
const fp1 = createHash("sha256").update(`${day1}${ccVersion}`).digest("hex").slice(0, 3);
|
|
const fp2 = createHash("sha256").update(`${day2}${ccVersion}`).digest("hex").slice(0, 3);
|
|
// They should be different (extremely high probability with SHA-256)
|
|
assert.notEqual(fp1, fp2, "Different days should produce different fingerprints");
|
|
});
|
|
});
|