Files
copilotkit__copilotkit/packages/cli/test/commands/tunnel.test.ts
Tyler Slaton 96885b5959 refactor: consolidate V1/V2 packages into flat @copilotkit/* structure
Flatten all packages from packages/v1/* and packages/v2/* into packages/* —
every package now lives directly under the @copilotkit/ scope with no v1/v2
subdirectories.

- Move all v1 packages (react-core, react-ui, runtime, shared, etc.) from
  packages/v1/* to packages/*
- Absorb v2 react code into packages/react-core/src/v2/ (exported via /v2 subpath)
- Absorb v2 agent code into packages/runtime/src/agent/ (exported via /v2 subpath)
- Move v2 packages (core, angular, demo-agents, etc.) to packages/*
- Replace all @copilotkitnext/* imports with @copilotkit/* equivalents
- Keep @copilotkitnext/angular as the sole exception (angular remains on next)
- Update CI workflows, renovate config, release scripts for flat structure
- No public API surface changes — all exports fields are preserved

Co-authored-by: Alem Tuzlak <t.zlak@hotmail.com>
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
2026-03-28 16:45:10 -07:00

145 lines
3.9 KiB
TypeScript

// Add at the top with other imports
declare global {
interface Process {
emit(event: "mockExit", code?: number): boolean;
once(event: "mockExit", listener: (code?: number) => void): Process;
}
}
import { expect } from "chai";
import { EventEmitter } from "node:events";
import sinon from "sinon";
import sinonChai from "sinon-chai";
import chai from "chai";
import { Config } from "@oclif/core";
import TunnelCreate from "../../src/commands/tunnel";
import { TunnelService } from "../../src/services/tunnel.service";
import { Tunnel } from "localtunnel";
chai.use(sinonChai);
class MockTunnel extends EventEmitter {
close = sinon.stub().resolves();
open = sinon.stub().resolves();
url = "https://test-subdomain.test-tunnel-host.com";
}
describe("tunnel", () => {
let mockTunnel: MockTunnel;
let tunnelService: TunnelService;
let config: Config;
let originalExit: (code?: number) => never;
beforeEach(async () => {
sinon.restore();
mockTunnel = new MockTunnel();
tunnelService = new TunnelService();
config = await Config.load();
// Stub TunnelService methods
sinon
.stub(tunnelService, "getMetaData")
.resolves({ tunnelHost: "test-tunnel-host.com" });
sinon
.stub(tunnelService, "create")
.resolves(mockTunnel as unknown as Tunnel);
// Store original process.exit
originalExit = process.exit;
// Stub process.exit to prevent test exit
sinon.stub(process, "exit").callsFake((code?: number) => {
// Instead of exiting, emit a custom event that we can listen for
// @ts-expect-error
process.emit("mockExit", code);
return undefined as never;
});
});
afterEach(() => {
sinon.restore();
// Restore original exit
process.exit = originalExit;
});
it("creates a tunnel with required port argument", (done) => {
const command = new TunnelCreate(["3000"], config, tunnelService);
// Listen for our custom mockExit event
process.once("mockExit", () => {
try {
// Verify the tunnel was created with correct options
expect(tunnelService.create).to.have.been.calledWith({
host: "test-tunnel-host.com",
port: 3000,
subdomain: undefined,
});
done();
} catch (error) {
done(error);
}
});
// Run the command and then emit close after a short delay
command.run().catch(done);
setTimeout(() => mockTunnel.emit("close"), 100);
});
it("fails without port argument", (done) => {
const command = new TunnelCreate([], config, tunnelService);
command.run().catch((error) => {
try {
expect(error.message).to.include("Missing 1 required arg");
expect(error.message).to.include("port");
done();
} catch (error) {
done(error);
}
});
});
it("creates tunnel with subdomain flag", (done) => {
const command = new TunnelCreate(
["3000", "--subdomain", "test-subdomain"],
config,
tunnelService,
);
process.once("mockExit", () => {
try {
expect(tunnelService.create).to.have.been.calledWith({
host: "test-tunnel-host.com",
port: 3000,
subdomain: "test-subdomain",
});
done();
} catch (error) {
done(error);
}
});
command.run().catch(done);
setTimeout(() => mockTunnel.emit("close"), 100);
});
it("handles SIGINT signal", (done) => {
const command = new TunnelCreate(["3000"], config, tunnelService);
process.once("mockExit", () => {
try {
expect(mockTunnel.close).to.have.been.called;
done();
} catch (error) {
done(error);
}
});
command.run().catch(done);
setTimeout(() => {
process.emit("SIGINT");
// Need to emit close after SIGINT to complete the test
setTimeout(() => mockTunnel.emit("close"), 100);
}, 100);
});
});