testIllTypedContributionIsRefusedAtMerge
A server takes code that doesn't typecheck on a contributor's branch, as a client can post it by hand (here: code saved where an implicit any is fine, sent saying it was checked strictly), and refuses to merge it, saying why.
import { codebase, LIB, startServer } from "#codebase/tcmts/cli/test/e2e/contributions"; import { tempDir, withChildren } from "#codebase/tcmts/cli/test/e2e/harness"; import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; export const testIllTypedContributionIsRefusedAtMerge = () => withChildren(async (keep) => { const root = tempDir("contributions types"); const server = codebase(root, "server"); server.write(LIB); server.tcm("add"); const url = await startServer(keep, server.dir, "tok"); const owner = codebase(root, "owner"); owner.tcm("remote", "add", "origin", url, "--token", "tok"); owner.tcm("pull"); const token = /their token \(shown once[^)]*\): (\S+)/.exec( owner.tcm("user", "add", "grace"), )![1]!; // grace's codebase checks loosely: there, a parameter with no type is fine const grace = codebase(root, "grace"); const tsconfig = path.join(grace.dir, "tsconfig.json"); const config = JSON.parse(fs.readFileSync(tsconfig, "utf8")) as { compilerOptions: Record<string, unknown>; }; config.compilerOptions = { ...config.compilerOptions, strict: false, noImplicitAny: false }; fs.writeFileSync(tsconfig, JSON.stringify(config, null, 2)); grace.tcm("remote", "add", "origin", url, "--token", token); grace.tcm("pull"); grace.tcm("branch", "loose"); grace.tcm("switch", "loose"); grace.write(`// tcm: namespace lib\nexport const same = (x) => x;\n`); grace.tcm("add"); // its record, sent by hand saying it was checked strictly (which options it was checked under isn't hashed) const graceUrl = await startServer(keep, grace.dir, "g"); const call = async (base: string, auth: string, route: string, body: unknown) => { const res = await fetch(`${base}/${route}`, { method: "POST", headers: { authorization: `Bearer ${auth}` }, body: JSON.stringify(body), }); return { status: res.status, body: (await res.json()) as any }; }; const names = (await call(graceUrl, "g", "branch", { name: "loose" })).body.branch.names; const hash = names.find((n: { path: string }) => n.path === "lib.same").hash as string; const [rec] = (await call(graceUrl, "g", "get", { hashes: [hash] })).body.records; assert.equal(rec.checked?.strict, false); const strict = { ...rec, checked: { ...rec.checked, strict: true, noImplicitAny: true } }; assert.deepEqual( (await call(url, token, "put", { records: [strict] })).body, { stored: 1 }, "received: it's what it says it is", ); assert.match( grace.tcm("contribute", "--title", "Loose"), /pushed origin\/@grace\/loose[\s\S]*opened contribution #1 on origin/, ); assert.match(server.tcm("ls", "lib", "--branch", "@grace/loose"), /lib\.same/); const merge = owner.run("contribution", "merge", "1"); assert.notEqual(merge.status, 0); assert.match(merge.out, /#1 doesn't typecheck, so it isn't merged/); assert.match(merge.out, /implicitly has an 'any' type/); assert.doesNotMatch(server.tcm("ls", "lib"), /lib\.same/, "main doesn't have it"); assert.match(owner.tcm("contribution"), /#1 +open +Loose/); })