test(google-meet): verify twilio setup readiness

This commit is contained in:
Peter Steinberger
2026-04-24 20:25:41 +01:00
parent 5c8a5fa8fa
commit 14934f0b7c
3 changed files with 163 additions and 2 deletions

View File

@@ -80,6 +80,7 @@ type NodeListResult = {
function setup(
config: Record<string, unknown> = {},
options: {
fullConfig?: Record<string, unknown>;
nodesListResult?: NodeListResult;
nodesInvokeResult?: unknown;
browserActResult?: Record<string, unknown>;
@@ -163,6 +164,7 @@ function setup(
description: "test",
version: "0",
source: "test",
config: options.fullConfig ?? {},
pluginConfig: config,
runtime: {
system: {
@@ -537,6 +539,94 @@ describe("google-meet plugin", () => {
expect(result.details.ok).toBe(true);
});
it("reports Twilio delegation readiness when voice-call is enabled", async () => {
vi.stubEnv("TWILIO_ACCOUNT_SID", "AC123");
vi.stubEnv("TWILIO_AUTH_TOKEN", "secret");
vi.stubEnv("TWILIO_FROM_NUMBER", "+15550001234");
const { tools } = setup(
{
defaultTransport: "chrome-node",
chromeNode: { node: "parallels-macos" },
},
{
fullConfig: {
plugins: {
allow: ["google-meet", "voice-call"],
entries: {
"voice-call": {
enabled: true,
config: { provider: "twilio" },
},
},
},
},
},
);
const tool = tools[0] as {
execute: (
id: string,
params: unknown,
) => Promise<{ details: { ok?: boolean; checks?: unknown[] } }>;
};
const result = await tool.execute("id", { action: "setup_status" });
expect(result.details.ok).toBe(true);
expect(result.details.checks).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "twilio-voice-call-plugin",
ok: true,
}),
expect.objectContaining({
id: "twilio-voice-call-credentials",
ok: true,
}),
]),
);
});
it("reports missing voice-call wiring for Twilio transport", async () => {
vi.stubEnv("TWILIO_ACCOUNT_SID", "");
vi.stubEnv("TWILIO_AUTH_TOKEN", "");
vi.stubEnv("TWILIO_FROM_NUMBER", "");
const { tools } = setup(
{ defaultTransport: "twilio" },
{
fullConfig: {
plugins: {
allow: ["google-meet"],
entries: {
"voice-call": { enabled: false },
},
},
},
},
);
const tool = tools[0] as {
execute: (
id: string,
params: unknown,
) => Promise<{ details: { ok?: boolean; checks?: unknown[] } }>;
};
const result = await tool.execute("id", { action: "setup_status" });
expect(result.details.ok).toBe(false);
expect(result.details.checks).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "twilio-voice-call-plugin",
ok: false,
}),
expect.objectContaining({
id: "twilio-voice-call-credentials",
ok: false,
}),
]),
);
});
it("launches Chrome after the BlackHole check", async () => {
const originalPlatform = process.platform;
Object.defineProperty(process, "platform", { value: "darwin" });

View File

@@ -81,7 +81,7 @@ export class GoogleMeetRuntime {
}
setupStatus() {
return getGoogleMeetSetupStatus(this.params.config);
return getGoogleMeetSetupStatus(this.params.config, { fullConfig: this.params.fullConfig });
}
async join(request: GoogleMeetJoinRequest): Promise<GoogleMeetJoinResult> {

View File

@@ -22,8 +22,32 @@ function resolveUserPath(input: string): string {
export function getGoogleMeetSetupStatus(config: GoogleMeetConfig): {
ok: boolean;
checks: SetupCheck[];
} {
};
export function getGoogleMeetSetupStatus(
config: GoogleMeetConfig,
options?: {
env?: NodeJS.ProcessEnv;
fullConfig?: unknown;
},
): {
ok: boolean;
checks: SetupCheck[];
};
export function getGoogleMeetSetupStatus(
config: GoogleMeetConfig,
options?: {
env?: NodeJS.ProcessEnv;
fullConfig?: unknown;
},
) {
const checks: SetupCheck[] = [];
const env = options?.env ?? process.env;
const fullConfig = asRecord(options?.fullConfig);
const pluginEntries = asRecord(asRecord(fullConfig.plugins).entries);
const pluginAllow = asRecord(fullConfig.plugins).allow;
const voiceCallEntry = asRecord(pluginEntries["voice-call"]);
const voiceCallConfig = asRecord(voiceCallEntry.config);
const voiceCallTwilioConfig = asRecord(voiceCallConfig.twilio);
if (config.auth.tokenPath) {
const tokenPath = resolveUserPath(config.auth.tokenPath);
@@ -110,8 +134,55 @@ export function getGoogleMeetSetupStatus(config: GoogleMeetConfig): {
: "Set chrome.waitForInCallMs to delay realtime intro until the Meet tab is in-call",
});
const shouldCheckTwilioDelegation =
config.voiceCall.enabled &&
(config.defaultTransport === "twilio" ||
Boolean(config.twilio.defaultDialInNumber) ||
Object.hasOwn(pluginEntries, "voice-call"));
if (shouldCheckTwilioDelegation) {
const voiceCallAllowed = !Array.isArray(pluginAllow) || pluginAllow.includes("voice-call");
const voiceCallEnabled = voiceCallEntry.enabled !== false;
checks.push({
id: "twilio-voice-call-plugin",
ok: voiceCallAllowed && voiceCallEnabled,
message:
voiceCallAllowed && voiceCallEnabled
? "Twilio transport can delegate dialing to the voice-call plugin"
: "Enable plugins.entries.voice-call and include voice-call in plugins.allow for Twilio dialing",
});
const provider = normalizeOptionalString(voiceCallConfig.provider) ?? "twilio";
if (provider === "twilio") {
const accountSid = normalizeOptionalString(voiceCallTwilioConfig.accountSid);
const authToken = normalizeOptionalString(voiceCallTwilioConfig.authToken);
const fromNumber = normalizeOptionalString(voiceCallConfig.fromNumber);
const twilioReady = Boolean(
(accountSid || env.TWILIO_ACCOUNT_SID) &&
(authToken || env.TWILIO_AUTH_TOKEN) &&
(fromNumber || env.TWILIO_FROM_NUMBER),
);
checks.push({
id: "twilio-voice-call-credentials",
ok: twilioReady,
message: twilioReady
? "Twilio voice-call credentials are configured"
: "Set TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_FROM_NUMBER or configure voice-call Twilio credentials",
});
}
}
return {
ok: checks.every((check) => check.ok),
checks,
};
}
function asRecord(value: unknown): Record<string, unknown> {
return value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: {};
}
function normalizeOptionalString(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}