testIllTypedContributionIsRefusedAtMerge

tcmts.cli.test.e2e.contributions.testIllTypedContributionIsRefusedAtMerge · #fzigqwlh · term

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/);
  })

uses