Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion extensions/ext-bundler-esbuild/src/es-module-lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
*/

import type { ImportSpecifier, ModuleLexer } from "veryfront/extensions/bundler";
import { init, parse } from "npm:es-module-lexer@2.3.1";
// Bare specifier, mapped to the pinned npm package by this extension's
// deno.json -- as `esbuild` is. Writing the `npm:` specifier inline instead
// resolves only under Deno, which is what broke `deno task test:bun`.
import { init, parse } from "es-module-lexer";

/** es-module-lexer-backed {@link ModuleLexer} implementation. */
export class EsModuleLexer implements ModuleLexer {
Expand Down
3 changes: 3 additions & 0 deletions tests/bun/run-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import os from "node:os";
import { readFileSync } from "node:fs";
import { filterTestFiles, listTestFiles, splitIntoShards } from "../test-file-utils.mjs";
import { ensureNpmNodeModulesLinks } from "../ensure-npm-links.mjs";
import { DENO_ONLY_TESTS } from "../deno-only-tests.mjs";

function resolveConcurrency(envKeys) {
for (const key of envKeys) {
Expand Down Expand Up @@ -45,6 +46,8 @@ const includePatterns = (process.env.BUN_TEST_INCLUDE || process.env.VF_TEST_INC
.map((value) => value.trim())
.filter(Boolean);
const runtimeIncompatibleTests = [
// Files the `Deno.`-in-source heuristic below cannot see; see the shared list.
...DENO_ONLY_TESTS,
"src/config/env.test.ts",
"src/proxy/handler.test.ts",
"src/proxy/oauth-client.test.ts",
Expand Down
25 changes: 25 additions & 0 deletions tests/deno-only-tests.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Test files that only make sense under `deno test`, shared by the Node and Bun
* runners.
*
* Both runners already drop a file whose own source mentions `Deno.`, which
* covers almost everything. It does not cover a file whose Deno usage lives in
* a helper it imports -- the heuristic reads one file, not the module graph --
* and it says nothing about files that are Deno-only by *subject* rather than
* by which API they happen to call.
*
* The pair below is both. It asserts a property of `deno test --parallel`
* itself: that test files sharing one process do not share a working directory.
* Node and Bun give each file its own process, so there is no property there to
* assert even if the APIs existed.
*
* Kept here rather than duplicated in each runner so the two cannot drift, and
* so it can be tested -- see ./runtime-test-filters.test.ts.
*
* @module tests/deno-only-tests
*/

/** Glob patterns for tests that must not run outside Deno. */
export const DENO_ONLY_TESTS = [
"src/testing/cwd-exclusion-*.test.ts",
];
11 changes: 9 additions & 2 deletions tests/node/run-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import os from "node:os";
import { readFileSync } from "node:fs";
import { filterTestFiles, listTestFiles, splitIntoShards } from "../test-file-utils.mjs";
import { ensureNpmNodeModulesLinks } from "../ensure-npm-links.mjs";
import { DENO_ONLY_TESTS } from "../deno-only-tests.mjs";

function resolveConcurrency(envKeys) {
for (const key of envKeys) {
Expand Down Expand Up @@ -43,8 +44,14 @@ const includePatterns = (process.env.NODE_TEST_INCLUDE || process.env.VF_TEST_IN
.split(",")
.map((value) => value.trim())
.filter(Boolean);
// Exclude Deno-specific test files that use Deno.test directly
const denoOnlyTests = ["src/issues/**", "src/cache/backend.test.ts"];
// Exclude Deno-specific test files that use Deno.test directly. Files the
// `Deno.`-in-source heuristic below cannot see live in ./deno-only-tests.mjs,
// shared with the Bun runner.
const denoOnlyTests = [
"src/issues/**",
"src/cache/backend.test.ts",
...DENO_ONLY_TESTS,
];
const runtimeIncompatibleTests = [
"src/proxy/handler.test.ts",
"src/proxy/oauth-client.test.ts",
Expand Down
61 changes: 61 additions & 0 deletions tests/runtime-test-filters.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* The Node and Bun runners must keep the Deno-only tests out, and everything
* else in.
*
* Both halves matter. A filter that excludes too little lets
* `src/testing/cwd-exclusion-*.test.ts` run on a runtime without `Deno.chdir`,
* which is the regression this list was added for. A filter that excludes too
* much silently shrinks the suite, which nothing else would notice.
*
* The list is easy to break by accident: renaming those files, or moving them
* out of `src/testing/`, leaves a pattern matching nothing and the runner fails
* again the next time someone runs `deno task test:node`. Neither task runs in
* CI, so this is the only thing standing between that and a surprised human.
*
* @module tests/runtime-test-filters
*/

import { describe, it } from "#veryfront/testing/bdd.ts";
import { assert, assertEquals } from "#veryfront/testing/assert.ts";
import { DENO_ONLY_TESTS } from "./deno-only-tests.mjs";
import { filterTestFiles } from "./test-file-utils.mjs";

/** The files the shared list exists to exclude. */
const DENO_ONLY_FILES = [
"src/testing/cwd-exclusion-a.test.ts",
"src/testing/cwd-exclusion-b.test.ts",
];

/** Ordinary tests, including neighbours of the excluded pair. */
const ELIGIBLE_FILES = [
"src/testing/cwd.test.ts",
"src/testing/isolation.test.ts",
"src/errors/error-registry.test.ts",
"cli/router.test.ts",
];

describe("runtime test filters", () => {
it("excludes the Deno-only tests from non-Deno runners", () => {
const kept = filterTestFiles(DENO_ONLY_FILES, { exclude: DENO_ONLY_TESTS });

assertEquals(kept, [], "these cannot run without Deno.chdir");
});

it("keeps every other test eligible", () => {
const kept = filterTestFiles(ELIGIBLE_FILES, { exclude: DENO_ONLY_TESTS });

// `cwd.test.ts` sits beside the excluded pair and starts with the same
// three letters, so an over-broad pattern would take it too.
assertEquals(kept, ELIGIBLE_FILES, "the filter must not shrink the suite");
});

it("matches files that actually exist", async () => {
// A renamed or moved file leaves a pattern matching nothing, and the
// exclusion silently stops working. Cheaper to catch here than in a failing
// `test:node` run.
for (const path of DENO_ONLY_FILES) {
const stat = await Deno.stat(new URL(`../${path}`, import.meta.url));
assert(stat.isFile, `${path} is named in the exclusion list but is missing`);
}
});
});