chore: fix dockerfile and unit tests

This commit is contained in:
Charlie Lamb
2026-06-12 13:53:33 +01:00
parent 6bb77582e9
commit 76f4d45fee
2 changed files with 165 additions and 211 deletions

View File

@@ -25,11 +25,11 @@ COPY apps/checkout/package.json apps/checkout/
COPY apps/docs/package.json apps/docs/
COPY apps/sdk-test/package.json apps/sdk-test/
COPY apps/website/package.json apps/website/
COPY packages/ai-sdk/package.json packages/ai-sdk/
COPY packages/atmn/package.json packages/atmn/
COPY packages/atmn-tests/package.json packages/atmn-tests/
COPY packages/auth/package.json packages/auth/
COPY packages/autumn-js/package.json packages/autumn-js/
COPY packages/gateway/package.json packages/gateway/
COPY packages/ksuid/package.json packages/ksuid/
COPY packages/logging/package.json packages/logging/
COPY packages/mcp/package.json packages/mcp/

View File

@@ -1,109 +1,11 @@
import { describe, expect, test } from "bun:test";
import type { PlanFilter } from "@autumn/shared";
import {
customerIdToStrings,
groupsToPlanFilter,
planFilterToGroups,
ruleToStringMatcher,
stringsToCustomerId,
} from "@/views/migrations/migration/filters/filterRowTypes";
describe("planFilterToGroups -> groupsToPlanFilter roundtrip", () => {
const roundtrip = (input: PlanFilter): PlanFilter => {
const groups = planFilterToGroups(input);
return groupsToPlanFilter(groups);
};
test("empty filter", () => {
expect(roundtrip({})).toEqual({});
});
test("plan_id bare string", () => {
expect(roundtrip({ plan_id: "pro" })).toEqual({ plan_id: "pro" });
});
test("plan_id $in", () => {
const input = { plan_id: { $in: ["a", "b"] } };
expect(roundtrip(input)).toEqual(input);
});
test("plan_id $nin", () => {
const input = { plan_id: { $nin: ["x"] } };
expect(roundtrip(input)).toEqual(input);
});
test("plan_id $ne", () => {
const input = { plan_id: { $ne: "old" } };
expect(roundtrip(input)).toEqual(input);
});
test("plan_id $regex", () => {
const input = { plan_id: { $regex: "^pro" } };
expect(roundtrip(input)).toEqual(input);
});
test("plan_id $startsWith", () => {
const input = { plan_id: { $startsWith: "pro" } };
expect(roundtrip(input)).toEqual(input);
});
test("paid: true", () => {
expect(roundtrip({ paid: true })).toEqual({ paid: true });
});
test("paid: false", () => {
expect(roundtrip({ paid: false })).toEqual({ paid: false });
});
test("recurring: true", () => {
expect(roundtrip({ recurring: true })).toEqual({ recurring: true });
});
test("custom: true", () => {
expect(roundtrip({ custom: true })).toEqual({ custom: true });
});
test("custom: false", () => {
expect(roundtrip({ custom: false })).toEqual({ custom: false });
});
test("price: null (free plan)", () => {
expect(roundtrip({ price: null })).toEqual({ price: null });
});
test("price: { $ne: null } (paid plan)", () => {
expect(roundtrip({ price: { $ne: null } })).toEqual({
price: { $ne: null },
});
});
test("item with implicit $some (feature_id)", () => {
const input: PlanFilter = { item: { feature_id: "credits" } };
expect(roundtrip(input)).toEqual(input);
});
test("item unlimited boolean", () => {
const input: PlanFilter = { item: { unlimited: true } };
expect(roundtrip(input)).toEqual(input);
});
test("$or groups", () => {
const input: PlanFilter = {
$or: [{ plan_id: "enterprise" }, { plan_id: "team" }],
};
expect(roundtrip(input)).toEqual(input);
});
test("combined filters", () => {
const input: PlanFilter = {
plan_id: "pro",
paid: true,
recurring: true,
item: { feature_id: "credits", unlimited: false },
};
expect(roundtrip(input)).toEqual(input);
});
});
describe("planFilterToGroups structure", () => {
test("empty filter produces one group with no rules", () => {
const groups = planFilterToGroups({});
@@ -111,40 +13,179 @@ describe("planFilterToGroups structure", () => {
expect(groups[0].rules).toHaveLength(0);
});
test("$or produces multiple groups", () => {
test("$or of pure plan selections collapses to a single plan row", () => {
const groups = planFilterToGroups({
$or: [{ plan_id: "a" }, { plan_id: "b" }],
});
expect(groups).toEqual([
{ rules: [{ field: "plan_id", operator: "in", values: ["a", "b"] }] },
]);
});
test("$or of non-selection filters produces multiple groups", () => {
const groups = planFilterToGroups({
$or: [{ paid: true }, { recurring: true }],
});
expect(groups).toHaveLength(2);
});
});
describe("groupsToPlanFilter", () => {
test("serializes visual OR groups as top-level $or branches", () => {
expect(
groupsToPlanFilter([
{
rules: [{ field: "custom", operator: "is", values: ["true"] }],
},
{
rules: [{ field: "custom", operator: "is", values: ["false"] }],
},
]),
).toEqual({
$or: [{ custom: true }, { custom: false }],
});
describe("planFilterToGroups decodes matcher forms", () => {
test("bare plan_id string", () => {
expect(planFilterToGroups({ plan_id: "pro" })).toEqual([
{ rules: [{ field: "plan_id", operator: "is", values: ["pro"] }] },
]);
});
test("ignores empty OR groups", () => {
test("plan_id $in", () => {
expect(planFilterToGroups({ plan_id: { $in: ["a", "b"] } })).toEqual([
{ rules: [{ field: "plan_id", operator: "in", values: ["a", "b"] }] },
]);
});
test("plan_id $ne", () => {
expect(planFilterToGroups({ plan_id: { $ne: "old" } })).toEqual([
{ rules: [{ field: "plan_id", operator: "is_not", values: ["old"] }] },
]);
});
test("plan_id $nin", () => {
expect(planFilterToGroups({ plan_id: { $nin: ["x"] } })).toEqual([
{ rules: [{ field: "plan_id", operator: "not_in", values: ["x"] }] },
]);
});
test("plan_id $regex", () => {
expect(planFilterToGroups({ plan_id: { $regex: "^pro" } })).toEqual([
{ rules: [{ field: "plan_id", operator: "regex", values: ["^pro"] }] },
]);
});
test("plan_id $startsWith", () => {
expect(planFilterToGroups({ plan_id: { $startsWith: "pro" } })).toEqual([
{
rules: [{ field: "plan_id", operator: "starts_with", values: ["pro"] }],
},
]);
});
test("booleans decode to is rules", () => {
expect(planFilterToGroups({ paid: true })).toEqual([
{ rules: [{ field: "paid", operator: "is", values: ["true"] }] },
]);
expect(planFilterToGroups({ custom: false })).toEqual([
{ rules: [{ field: "custom", operator: "is", values: ["false"] }] },
]);
});
test("price null decodes to not_exists, $ne null to exists", () => {
expect(planFilterToGroups({ price: null })).toEqual([
{ rules: [{ field: "price", operator: "not_exists", values: [] }] },
]);
expect(planFilterToGroups({ price: { $ne: null } })).toEqual([
{ rules: [{ field: "price", operator: "exists", values: [] }] },
]);
});
test("item filters decode to item_* rules", () => {
expect(
groupsToPlanFilter([
{
rules: [{ field: "custom", operator: "is", values: ["true"] }],
},
{ rules: [{ field: "plan_id", operator: "is", values: [] }] },
]),
).toEqual({ custom: true });
planFilterToGroups({ item: { feature_id: "credits", unlimited: false } }),
).toEqual([
{
rules: [
{ field: "item_feature_id", operator: "is", values: ["credits"] },
{ field: "item_unlimited", operator: "is", values: ["false"] },
],
},
]);
});
test("combined filters share one group", () => {
const groups = planFilterToGroups({
plan_id: { $regex: "^pro" },
paid: true,
recurring: true,
});
expect(groups).toHaveLength(1);
expect(groups[0].rules.map((rule) => rule.field)).toEqual([
"plan_id",
"paid",
"recurring",
]);
});
});
describe("ruleToStringMatcher", () => {
test("is with one value returns a bare string", () => {
expect(
ruleToStringMatcher({ field: "plan_id", operator: "is", values: ["x"] }),
).toBe("x");
});
test("is with no values returns an empty string", () => {
expect(
ruleToStringMatcher({ field: "plan_id", operator: "is", values: [] }),
).toBe("");
});
test("is with multiple values widens to $in", () => {
expect(
ruleToStringMatcher({
field: "plan_id",
operator: "is",
values: ["a", "b"],
}),
).toEqual({ $in: ["a", "b"] });
});
test("is_not returns $ne", () => {
expect(
ruleToStringMatcher({
field: "plan_id",
operator: "is_not",
values: ["x"],
}),
).toEqual({ $ne: "x" });
});
test("in returns $in", () => {
expect(
ruleToStringMatcher({
field: "plan_id",
operator: "in",
values: ["a", "b"],
}),
).toEqual({ $in: ["a", "b"] });
});
test("not_in returns $nin", () => {
expect(
ruleToStringMatcher({
field: "plan_id",
operator: "not_in",
values: ["x"],
}),
).toEqual({ $nin: ["x"] });
});
test("regex returns $regex", () => {
expect(
ruleToStringMatcher({
field: "plan_id",
operator: "regex",
values: ["^pro"],
}),
).toEqual({ $regex: "^pro" });
});
test("starts_with returns $startsWith", () => {
expect(
ruleToStringMatcher({
field: "plan_id",
operator: "starts_with",
values: ["pro"],
}),
).toEqual({ $startsWith: "pro" });
});
});
@@ -225,90 +266,3 @@ describe("customerIdToStrings -> stringsToCustomerId roundtrip", () => {
expect(stringsToCustomerId(customerIdToStrings(undefined))).toBeUndefined();
});
});
describe("planFilterToGroups -> groupsToPlanFilter: version (NumberMatcher)", () => {
const roundtrip = (input: PlanFilter): PlanFilter => {
const groups = planFilterToGroups(input);
return groupsToPlanFilter(groups);
};
test("bare number roundtrips", () => {
expect(roundtrip({ version: 2 })).toEqual({ version: 2 });
});
test("explicit null roundtrips", () => {
// Regression: previously the UI dropped `version: null` because
// ruleToNumberMatcher returned undefined for an empty values array.
expect(roundtrip({ version: null })).toEqual({ version: null });
});
test("$eq number roundtrips (simplified to bare value)", () => {
expect(roundtrip({ version: { $eq: 3 } })).toEqual({ version: 3 });
});
test("$ne number roundtrips", () => {
const input = { version: { $ne: 1 } };
expect(roundtrip(input)).toEqual(input);
});
test("$in roundtrips", () => {
const input = { version: { $in: [1, 2, 3] } };
expect(roundtrip(input)).toEqual(input);
});
test("$nin roundtrips", () => {
const input = { version: { $nin: [4, 5] } };
expect(roundtrip(input)).toEqual(input);
});
test("$gte roundtrips", () => {
const input = { version: { $gte: 2 } };
expect(roundtrip(input)).toEqual(input);
});
test("$lt roundtrips", () => {
const input = { version: { $lt: 5 } };
expect(roundtrip(input)).toEqual(input);
});
test("$gte + $lte (range) preserves BOTH constraints", () => {
// Regression: previously emitted only the first operator, silently
// broadening "versions 24" to "versions ≥ 2".
const input = { version: { $gte: 2, $lte: 4 } };
expect(roundtrip(input)).toEqual(input);
});
test("$gt + $lt (open range) preserves BOTH constraints", () => {
const input = { version: { $gt: 1, $lt: 10 } };
expect(roundtrip(input)).toEqual(input);
});
});
describe("planFilterToGroups: version emits multiple rules for combined matchers", () => {
test("single operator → single rule", () => {
const groups = planFilterToGroups({ version: { $gte: 2 } });
const versionRules = groups[0].rules.filter((r) => r.field === "version");
expect(versionRules.length).toBe(1);
expect(versionRules[0]).toEqual({
field: "version",
operator: "gte",
values: ["2"],
});
});
test("$gte + $lte → two rules in the same group", () => {
const groups = planFilterToGroups({ version: { $gte: 2, $lte: 4 } });
const versionRules = groups[0].rules.filter((r) => r.field === "version");
expect(versionRules.length).toBe(2);
const operators = versionRules.map((r) => r.operator).sort();
expect(operators).toEqual(["gte", "lte"]);
});
test("null → single empty `is` rule", () => {
const groups = planFilterToGroups({ version: null });
const versionRules = groups[0].rules.filter((r) => r.field === "version");
expect(versionRules).toEqual([
{ field: "version", operator: "is", values: [] },
]);
});
});