-
+
+
+
+
{{ $t("configuration.bird.config_unavailable") }}
+
+
+
+
+
+ {{ $t(`configuration.bird.state.${healthState}`) }}
+
+
{{ $t("configuration.bird.read_only_health") }}
+
+
+
+
+
- {{ $t("configuration.bird.workspace_id") }}
+ - {{ health.workspaceId || configuredWorkspaceId || $t("configuration.bird.not_configured") }}
+
+
+
- {{ $t("configuration.bird.channel_id") }}
+ - {{ health.channelId || getModuleConfigValue("channelId") || $t("configuration.bird.not_configured") }}
+
+
+
- {{ $t("configuration.bird.provider_check") }}
+ - {{ health.check || $t("configuration.bird.not_available") }}
+
+
+
+
+ -
+ {{ channel.name || channel.id || channel.channelId || $t("configuration.bird.unnamed_channel") }}
+ {{ channel.platform }}
+ {{ channel.status }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/unit/bird-configuration.spec.js b/tests/unit/bird-configuration.spec.js
new file mode 100644
index 00000000..69ad10c8
--- /dev/null
+++ b/tests/unit/bird-configuration.spec.js
@@ -0,0 +1,216 @@
+// @vitest-environment jsdom
+
+import { flushPromises, mount } from "@vue/test-utils";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+const birdMocks = vi.hoisted(() => ({
+ getAll: vi.fn(),
+ getStatus: vi.fn(),
+}));
+
+vi.mock("@/components/displays/superuser/configuration/ConfigurationCategory.vue", () => ({
+ default: {
+ props: ["title", "subtitle"],
+ template: '
{{ title }}
{{ subtitle }}
',
+ },
+}));
+vi.mock("@/components/displays/superuser/configuration/ConfigurationInput.vue", () => ({
+ default: {
+ props: ["title", "value"],
+ template: '
{{ title }}:{{ value }}
',
+ },
+}));
+vi.mock("@/components/displays/superuser/configuration/ConfigurationSecretKey.vue", () => ({
+ default: {
+ props: ["isSet"],
+ template: '
{{ isSet ? "set" : "unset" }}
',
+ },
+}));
+vi.mock("@/components/displays/superuser/configuration/ConfigurationSwitch.vue", () => ({
+ default: { template: "
" },
+}));
+vi.mock("@/components/global/PageTitle.vue", () => ({
+ default: { template: "
" },
+}));
+vi.mock("@/components/page/wrappers/RestrictedPageWrapper.vue", () => ({
+ default: { template: "
" },
+}));
+vi.mock("@/views/dashboards/superUserDashboard/configuration/ConfigurationSubPageWrapper.vue", () => ({
+ default: { template: '
' },
+}));
+
+vi.mock("@/components/session/token/SessionUser.vue", () => ({
+ SessionUser: {
+ canAccessSuperUser: () => true,
+ superUser: {
+ modules: {
+ bird: {
+ config: {
+ get_all: birdMocks.getAll,
+ enabled: { set: vi.fn() },
+ keys: {
+ api_key: { set: vi.fn() },
+ server_url: { set: vi.fn() },
+ workspaceId: { set: vi.fn() },
+ channelId: { set: vi.fn() },
+ participantId: { set: vi.fn() },
+ allowed_channel_ids_json: { set: vi.fn() },
+ control_plane_enabled: { set: vi.fn() },
+ control_plane_token: { set: vi.fn() },
+ webhook_signing_key: { set: vi.fn() },
+ webhook_public_url: { set: vi.fn() },
+ webhook_replay_window_seconds: { set: vi.fn() },
+ flow_enabled: { set: vi.fn() },
+ flow_shared_secret: { set: vi.fn() },
+ flow_policy_json: { set: vi.fn() },
+ operations_actions_enabled: { set: vi.fn() },
+ outbound_messages_enabled: { set: vi.fn() },
+ template_policy_json: { set: vi.fn() },
+ },
+ },
+ functions: {
+ getHealth: birdMocks.getStatus,
+ },
+ },
+ },
+ },
+ },
+}));
+
+import ConfigurationBird from "@/views/dashboards/superUserDashboard/configuration/ConfigurationBird.vue";
+
+const global = {
+ mocks: {
+ $t: (key) => key,
+ },
+};
+
+describe("Bird configuration", () => {
+ beforeEach(() => {
+ birdMocks.getAll.mockReset();
+ birdMocks.getStatus.mockReset();
+ });
+
+ it("renders healthy read-only status and provider channel state", async () => {
+ birdMocks.getAll.mockResolvedValue({
+ data: {
+ data: {
+ enabled: { variable: "enabled", value: true },
+ workspaceId: { variable: "workspaceId", value: "workspace-current" },
+ },
+ },
+ });
+ birdMocks.getStatus.mockResolvedValue({
+ data: {
+ data: {
+ enabled: true,
+ configured: true,
+ providerReachable: true,
+ workspaceId: "workspace-current",
+ channelId: "channel-1",
+ check: "channels.list",
+ channels: [{ id: "channel-1", name: "Customer WhatsApp", platform: "whatsapp", status: "active" }],
+ },
+ },
+ });
+
+ const wrapper = mount(ConfigurationBird, { global });
+ await flushPromises();
+
+ expect(wrapper.get('[data-testid="bird-health-state"]').text()).toBe("configuration.bird.state.healthy");
+ expect(wrapper.text()).toContain("configuration.bird.read_only_health");
+ expect(wrapper.text()).toContain("channels.list");
+ expect(wrapper.text()).toContain("Customer WhatsApp");
+ expect(wrapper.text()).toContain("whatsapp");
+ expect(wrapper.text()).toContain("active");
+ });
+
+ it("uses workplaceId only as a display fallback while exposing the canonical field", async () => {
+ birdMocks.getAll.mockResolvedValue({
+ data: {
+ data: {
+ enabled: { variable: "enabled", value: false },
+ workplaceId: { variable: "workplaceId", value: "workspace-legacy" },
+ },
+ },
+ });
+ birdMocks.getStatus.mockResolvedValue({
+ data: {
+ data: {
+ enabled: false,
+ configured: false,
+ healthy: false,
+ },
+ },
+ });
+
+ const wrapper = mount(ConfigurationBird, { global });
+ await flushPromises();
+
+ expect(wrapper.get('[data-testid="bird-health-state"]').text()).toBe("configuration.bird.state.disabled");
+ expect(wrapper.text()).toContain("configuration.bird.workspace_id:workspace-legacy");
+ });
+
+ it("fails closed to an unavailable health state", async () => {
+ birdMocks.getAll.mockRejectedValue(new Error("Config unavailable"));
+ birdMocks.getStatus.mockRejectedValue(new Error("Health unavailable"));
+
+ const wrapper = mount(ConfigurationBird, { global });
+ await flushPromises();
+
+ expect(wrapper.get('[data-testid="bird-config-unavailable"]').text()).toBe("configuration.bird.config_unavailable");
+ expect(wrapper.get('[data-testid="bird-health-state"]').text()).toBe("configuration.bird.state.unavailable");
+ });
+
+ it("uses redacted secret metadata and never renders an accidentally returned secret value", async () => {
+ birdMocks.getAll.mockResolvedValue({
+ data: {
+ data: {
+ enabled: { variable: "enabled", value: true },
+ api_key: {
+ variable: "api_key",
+ value: "must-not-reach-the-dom",
+ isSecret: true,
+ isSet: true,
+ },
+ },
+ },
+ });
+ birdMocks.getStatus.mockResolvedValue({
+ data: {
+ data: {
+ enabled: true,
+ configured: true,
+ healthy: true,
+ channels: [],
+ },
+ },
+ });
+
+ const wrapper = mount(ConfigurationBird, { global });
+ await flushPromises();
+
+ expect(wrapper.get('[data-testid="bird-secret-state"]').text()).toBe("set");
+ expect(wrapper.text()).not.toContain("must-not-reach-the-dom");
+ });
+
+ it("redacts the legacy API key even before isSecret metadata is deployed", async () => {
+ birdMocks.getAll.mockResolvedValue({
+ data: {
+ data: {
+ enabled: { variable: "enabled", value: true },
+ api_key: { variable: "api_key", value: "legacy-raw-secret" },
+ },
+ },
+ });
+ birdMocks.getStatus.mockResolvedValue({
+ data: { data: { enabled: true, configured: true, healthy: true, channels: [] } },
+ });
+
+ const wrapper = mount(ConfigurationBird, { global });
+ await flushPromises();
+
+ expect(wrapper.get('[data-testid="bird-secret-state"]').text()).toBe("set");
+ expect(wrapper.text()).not.toContain("legacy-raw-secret");
+ });
+});
diff --git a/tests/unit/bird-module.spec.js b/tests/unit/bird-module.spec.js
new file mode 100644
index 00000000..24ef92ab
--- /dev/null
+++ b/tests/unit/bird-module.spec.js
@@ -0,0 +1,86 @@
+import { readFileSync } from "node:fs";
+import { join } from "node:path";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+const authenticatedRequestMock = vi.hoisted(() => vi.fn());
+
+vi.mock("@/components/session/authenticatedRequest.vue", () => ({
+ authenticatedRequest: authenticatedRequestMock,
+}));
+
+import { Bird } from "@/components/session/token/superUser/modules/bird/Bird.vue";
+import { Config } from "@/components/session/token/superUser/modules/bird/Config.vue";
+
+const root = process.cwd();
+const pageSource = readFileSync(
+ join(root, "src/views/dashboards/superUserDashboard/configuration/ConfigurationBird.vue"),
+ "utf8"
+);
+
+describe("Bird module contract", () => {
+ beforeEach(() => {
+ authenticatedRequestMock.mockReset();
+ authenticatedRequestMock.mockResolvedValue({ data: { data: {} } });
+ });
+
+ it("uses the end-user read-only health endpoint for configuration health", async () => {
+ await Bird.functions.getHealth();
+
+ expect(authenticatedRequestMock).toHaveBeenCalledOnce();
+ expect(authenticatedRequestMock).toHaveBeenCalledWith("/bird/health", "GET");
+ });
+
+ it("writes the canonical workspaceId while retaining the legacy workplaceId adapter", async () => {
+ await Config.keys.workspaceId.set("workspace-canonical");
+ await Config.keys.workplaceId.get();
+
+ expect(authenticatedRequestMock).toHaveBeenNthCalledWith(1, "/bird/config", "POST", {
+ variable: "workspaceId",
+ value: "workspace-canonical",
+ });
+ expect(authenticatedRequestMock).toHaveBeenNthCalledWith(2, "/bird/config?variable=workplaceId", "GET");
+ });
+
+ it("exposes protected gateway and channel configuration through typed config keys", async () => {
+ await Config.keys.allowed_channel_ids_json.set('["channel-1"]');
+ await Config.keys.control_plane_enabled.set(true);
+ await Config.keys.webhook_signing_key.set("rotated-secret");
+ await Config.keys.participantId.set("participant-1");
+
+ expect(authenticatedRequestMock).toHaveBeenNthCalledWith(1, "/bird/config", "POST", {
+ variable: "allowed_channel_ids_json",
+ value: '["channel-1"]',
+ });
+ expect(authenticatedRequestMock).toHaveBeenNthCalledWith(2, "/bird/config", "POST", {
+ variable: "control_plane_enabled",
+ value: true,
+ });
+ expect(authenticatedRequestMock).toHaveBeenNthCalledWith(3, "/bird/config", "POST", {
+ variable: "webhook_signing_key",
+ value: "rotated-secret",
+ });
+ expect(authenticatedRequestMock).toHaveBeenNthCalledWith(4, "/bird/config", "POST", {
+ variable: "participantId",
+ value: "participant-1",
+ });
+ });
+
+ it("does not place a fixed-number call from the configuration page", () => {
+ expect(pageSource).toContain("getHealth");
+ expect(pageSource).not.toContain("getControlPlaneStatus");
+ expect(pageSource).toContain('getModuleConfigValue("workspaceId") || getModuleConfigValue("workplaceId")');
+ expect(pageSource).toContain("config.keys.workspaceId.set");
+ expect(pageSource).toContain("BIRD_SECRET_VARIABLES.has(value.variable)");
+ expect(pageSource).toContain("isModuleSecretSet('api_key')");
+ expect(pageSource).not.toContain("testOutboundVoiceCall");
+ expect(pageSource).not.toContain("console.");
+ });
+
+ it("renders health, disabled, channel, and read-only states through i18n keys", () => {
+ expect(pageSource).toContain("configuration.bird.health_title");
+ expect(pageSource).toContain("configuration.bird.read_only_health");
+ expect(pageSource).toContain("configuration.bird.state.");
+ expect(pageSource).toContain("normalizedChannels");
+ expect(pageSource).toContain('data-testid="bird-health-state"');
+ });
+});