Bernt Christian Egeland пре 1 месец
родитељ
комит
c75a5429e0
2 измењених фајлова са 65 додато и 4 уклоњено
  1. 22 4
      prisma/seed_dummy_data.ts
  2. 43 0
      src/__tests__/lib/seed-image-imports.test.ts

+ 22 - 4
prisma/seed_dummy_data.ts

@@ -1,8 +1,4 @@
 import { PrismaClient } from "../src/generated/prisma/client";
 import { PrismaClient } from "../src/generated/prisma/client";
-import {
-  TIRE_ROAD_POSITIONS,
-  gradeTread,
-} from "../src/features/tire-hotel/Lib/tireConstants";
 import { PrismaPg } from "@prisma/adapter-pg";
 import { PrismaPg } from "@prisma/adapter-pg";
 import { randomBytes, scryptSync } from "node:crypto";
 import { randomBytes, scryptSync } from "node:crypto";
 import * as fs from "fs";
 import * as fs from "fs";
@@ -76,6 +72,28 @@ const DEMO_ORG_NAME = process.env.DEMO_ORG_NAME || "Demo Auto Workshop";
 /// unit system, and used both for the settings rows and for grading the
 /// unit system, and used both for the settings rows and for grading the
 /// seeded readings, so the two can never disagree.
 /// seeded readings, so the two can never disagree.
 const DEMO_TREAD_LIMITS = { summerReplace: 1.6, winterReplace: 4, warnMargin: 1 };
 const DEMO_TREAD_LIMITS = { summerReplace: 1.6, winterReplace: 4, warnMargin: 1 };
+
+// Copied from src/features/tire-hotel/Lib/tireConstants rather than imported,
+// for the same reason as the inspection templates further down: the production
+// image ships only prisma/ and src/generated (see Dockerfile), so an import
+// reaching into src/features fails the deploy job with MODULE_NOT_FOUND. That
+// file stays the source of truth — change it there first, then mirror it here.
+/** The four road positions, in the order a technician walks around a car. */
+const TIRE_ROAD_POSITIONS = ["front_left", "front_right", "rear_left", "rear_right"] as const;
+
+type SeedTreadThresholds = { summerReplace: number; winterReplace: number; warnMargin: number };
+
+function gradeTread(
+  treadDepthMm: number | null | undefined,
+  season: string,
+  thresholds: SeedTreadThresholds,
+): "good" | "fair" | "replace" | null {
+  if (treadDepthMm == null) return null;
+  const limit = season === "winter" ? thresholds.winterReplace : thresholds.summerReplace;
+  if (treadDepthMm < limit) return "replace";
+  if (treadDepthMm < limit + thresholds.warnMargin) return "fair";
+  return "good";
+}
 const DATA_ROOT = process.env.DATA_ROOT || path.join(process.cwd(), "data");
 const DATA_ROOT = process.env.DATA_ROOT || path.join(process.cwd(), "data");
 const UPLOAD_DIR = path.join(DATA_ROOT, "uploads", ORG_ID, "vehicles");
 const UPLOAD_DIR = path.join(DATA_ROOT, "uploads", ORG_ID, "vehicles");
 // Image assets bundled with the repo — preferred over live URL downloads so
 // Image assets bundled with the repo — preferred over live URL downloads so

+ 43 - 0
src/__tests__/lib/seed-image-imports.test.ts

@@ -0,0 +1,43 @@
+import fs from 'node:fs'
+import { describe, expect, it } from 'vitest'
+
+/**
+ * The demo seed runs inside the production image, and that image is not the
+ * repository. Its Dockerfile copies `prisma/` and `src/generated` and nothing
+ * else, so an import reaching anywhere further into `src/` type-checks here,
+ * passes review, and then kills the deploy job with MODULE_NOT_FOUND against a
+ * file that plainly exists on the developer's disk.
+ *
+ * It has happened twice: once for the inspection template presets, once for
+ * the tire hotel constants. Both are duplicated into the seed now, with the
+ * original left as the source of truth. This is the check that stops a third.
+ */
+
+const SEED = 'prisma/seed_dummy_data.ts'
+
+/** Directories the image actually ships, relative to the repository root. */
+const SHIPPED = ['src/generated/']
+
+describe('the demo seed', () => {
+  it('imports nothing the production image leaves behind', () => {
+    const source = fs.readFileSync(SEED, 'utf-8')
+
+    // Covers `import ... from '../src/x'` and `await import('../src/x')`.
+    const specifiers = [...source.matchAll(/(?:from|import)\s*\(?\s*["']([^"']+)["']/g)].map(
+      (match) => match[1]
+    )
+
+    const reachesIntoSrc = specifiers
+      .filter((specifier) => specifier.includes('src/'))
+      .map((specifier) => specifier.replace(/^(?:\.\.?\/)+/, ''))
+      .filter((specifier) => !SHIPPED.some((dir) => specifier.startsWith(dir)))
+
+    expect(
+      reachesIntoSrc,
+      `${SEED} imports these, which the production image does not ship:\n` +
+        `${reachesIntoSrc.join('\n')}\n` +
+        `Copy what it needs into the seed instead, as the file already does for ` +
+        `the inspection templates and the tire constants.`
+    ).toEqual([])
+  })
+})