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: 5 additions & 0 deletions .changeset/runnable-examples-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fluent-effect": patch
---

Add runnable Bun smoke coverage for every TypeScript example.
1 change: 1 addition & 0 deletions conventions/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ Use descriptive test names.
Group related tests using describe blocks.
Aim for high code coverage.
Avoid duplication of testing scenarios.
Examples under `examples/` are executed by `bun run examples:smoke`; keep them deterministic, fast, and free of real network calls.
25 changes: 18 additions & 7 deletions examples/batch-job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@ interface User {
readonly name: string;
}

interface UserApi {
readonly fetchUser: (id: string, signal: AbortSignal) => Promise<User>;
}

const AppError = fx.errors<{
NetworkError: { cause: unknown };
TimeoutError: { operation: string };
}>();

const UserApi = fx.dependency<UserApi>("UserApi");

const fetchUser = (id: string) =>
fx.try({
try: async (signal) => {
const response = await fetch(`https://example.com/users/${id}`, { signal });
return (await response.json()) as User;
},
catch: (cause) => AppError.NetworkError({ cause }),
fx.task(function* () {
const userApi = yield* fx.getDependency(UserApi);

return yield* fx.try({
try: (signal) => userApi.fetchUser(id, signal),
catch: (cause) => AppError.NetworkError({ cause }),
});
});

const loadUser = (id: string) =>
Expand Down Expand Up @@ -48,4 +55,8 @@ const loadUsers = (ids: readonly string[]) =>
return users;
});

export const main = fx.run(loadUsers(["1", "2", "3"]));
const stubUserApi = fx.provideDependency(UserApi, {
fetchUser: async (id) => ({ id, name: `User ${id}` }),
});

export const main = fx.runWith(loadUsers(["1", "2", "3"]), fx.dependencies(stubUserApi));
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
},
"scripts": {
"test": "bun test",
"examples:smoke": "bun test test/examples-smoke.test.ts",
"lint": "oxlint . --type-aware",
"lint:fix": "oxlint . --type-aware --fix",
"fmt": "oxfmt .",
Expand Down
21 changes: 21 additions & 0 deletions test/examples-smoke.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { $ } from "bun";
import { describe, expect, test } from "bun:test";
import { readdirSync } from "node:fs";
import { join } from "node:path";

const repositoryDirectory = join(import.meta.dir, "..");
const examplesDirectory = join(repositoryDirectory, "examples");

const exampleFiles = readdirSync(examplesDirectory)
.filter((file) => file.endsWith(".ts"))
.sort();

describe("examples smoke tests", () => {
expect(exampleFiles.length).toBeGreaterThan(0);

for (const exampleFile of exampleFiles) {
test(`${exampleFile} executes with Bun`, async () => {
await $`bun ${join(examplesDirectory, exampleFile)}`.cwd(repositoryDirectory).quiet();
});
}
});
Loading