import type { Logger } from "@dispatch/kernel";
import { describe, expect, it } from "vitest";
import { loadExternalExtensions } from "./load-external.js";

function makeLogger(): Logger {
	const noop = () => {};
	const logger = {
		debug: noop,
		info: noop,
		warn: noop,
		error: noop,
		child: () => logger,
		span: () => {
			throw new Error("not used");
		},
	} as unknown as Logger;
	return logger;
}

describe("loadExternalExtensions", () => {
	it("returns an empty array for no specifiers", async () => {
		expect(await loadExternalExtensions([], makeLogger())).toEqual([]);
	});

	it("loads a real extension module by package name", async () => {
		// auth-apikey is a bundled extension that exports `extension`; we use it as
		// a stand-in for an external module to exercise the dynamic-import path.
		const loaded = await loadExternalExtensions(["@dispatch/auth-apikey"], makeLogger());
		expect(loaded).toHaveLength(1);
		expect(loaded[0]?.manifest.id).toBe("auth-apikey");
	});

	it("skips a specifier that cannot be imported without throwing", async () => {
		const loaded = await loadExternalExtensions(
			["./does-not-exist-xyz.js", "@dispatch/auth-apikey"],
			makeLogger(),
		);
		// The bad one is skipped; the good one still loads.
		expect(loaded.map((e) => e.manifest.id)).toEqual(["auth-apikey"]);
	});

	it("skips a module that exports no valid extension", async () => {
		// `@dispatch/journal-sink` exports factories but no `extension`.
		const loaded = await loadExternalExtensions(["@dispatch/journal-sink"], makeLogger());
		expect(loaded).toEqual([]);
	});
});
