Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/JAVA/Openclaw/extensions/msteams/src/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 6 kB image not shown  

Quelle  resolve-allowlist.test.ts

  Sprache: JAVA
 

import { describe, expect, it, vi } from "vitest";

const {
  listTeamsByName,
  listChannelsForTeam,
  normalizeQuery,
  resolveGraphToken,
  searchGraphUsers,
} = vi.hoisted(() => ({
  listTeamsByName: vi.fn(),
  listChannelsForTeam: vi.fn(),
  normalizeQuery: vi.fn((value: string) => value.trim().toLowerCase()),
  resolveGraphToken: vi.fn(async () => "graph-token"),
  searchGraphUsers: vi.fn(),
}));

vi.mock("./graph.js", () => ({
  listTeamsByName,
  listChannelsForTeam,
  normalizeQuery,
  resolveGraphToken,
}));

vi.mock("./graph-users.js", () => ({
  searchGraphUsers,
}));

import {
  looksLikeMSTeamsTargetId,
  resolveMSTeamsChannelAllowlist,
  resolveMSTeamsUserAllowlist,
} from "./resolve-allowlist.js";

describe("resolveMSTeamsUserAllowlist", () => {
  it("marks empty input unresolved", async () => {
    const [result] = await resolveMSTeamsUserAllowlist({ cfg: {}, entries: ["  "] });
    expect(result).toEqual({ input: "  ", resolved: false });
  });

  it("resolves first Graph user match", async () => {
    searchGraphUsers.mockResolvedValueOnce([
      { id: "user-1", displayName: "Alice One" },
      { id: "user-2", displayName: "Alice Two" },
    ]);
    const [result] = await resolveMSTeamsUserAllowlist({ cfg: {}, entries: ["alice"] });
    expect(result).toEqual({
      input: "alice",
      resolved: true,
      id: "user-1",
      name: "Alice One",
      note: "multiple matches; chose first",
    });
  });
});

describe("resolveMSTeamsChannelAllowlist", () => {
  it("resolves team/channel by team name + channel display name", async () => {
    // After the fix, listChannelsForTeam is called once and reused for both
    // General channel resolution and channel matching.
    listTeamsByName.mockResolvedValueOnce([{ id: "team-guid-1", displayName: "Product Team" }]);
    listChannelsForTeam.mockResolvedValueOnce([
      { id: "19:general-conv-id@thread.tacv2", displayName: "General" },
      { id: "19:roadmap-conv-id@thread.tacv2", displayName: "Roadmap" },
    ]);

    const [result] = await resolveMSTeamsChannelAllowlist({
      cfg: {},
      entries: ["Product Team/Roadmap"],
    });

    // teamId is now the General channel's conversation ID — not the Graph GUID —
    // because that's what Bot Framework sends as channelData.team.id at runtime.
    expect(result).toEqual({
      input: "Product Team/Roadmap",
      resolved: true,
      teamId: "19:general-conv-id@thread.tacv2",
      teamName: "Product Team",
      channelId: "19:roadmap-conv-id@thread.tacv2",
      channelName: "Roadmap",
      note: "multiple channels; chose first",
    });
  });

  it("uses General channel conversation ID as team key for team-only entry", async () => {
    // When no channel is specified we still resolve the General channel so the
    // stored key matches what Bot Framework sends as channelData.team.id.
    listTeamsByName.mockResolvedValueOnce([{ id: "guid-engineering", displayName: "Engineering" }]);
    listChannelsForTeam.mockResolvedValueOnce([
      { id: "19:eng-general@thread.tacv2", displayName: "General" },
      { id: "19:eng-standups@thread.tacv2", displayName: "Standups" },
    ]);

    const [result] = await resolveMSTeamsChannelAllowlist({
      cfg: {},
      entries: ["Engineering"],
    });

    expect(result).toEqual({
      input: "Engineering",
      resolved: true,
      teamId: "19:eng-general@thread.tacv2",
      teamName: "Engineering",
    });
  });

  it("falls back to Graph GUID when listChannelsForTeam throws", async () => {
    // Edge case: API call fails (rate limit, network error). We fall back to
    // the Graph GUID as the team key — the pre-fix behavior — so resolution
    // still succeeds instead of propagating the error.
    listTeamsByName.mockResolvedValueOnce([{ id: "guid-flaky", displayName: "Flaky Team" }]);
    listChannelsForTeam.mockRejectedValueOnce(new Error("429 Too Many Requests"));

    const [result] = await resolveMSTeamsChannelAllowlist({
      cfg: {},
      entries: ["Flaky Team"],
    });

    expect(result).toEqual({
      input: "Flaky Team",
      resolved: true,
      teamId: "guid-flaky",
      teamName: "Flaky Team",
    });
  });

  it("falls back to Graph GUID when General channel is not found", async () => {
    // Edge case: General channel was renamed or deleted. We fall back to the
    // Graph GUID so resolution still succeeds rather than silently breaking.
    listTeamsByName.mockResolvedValueOnce([{ id: "guid-ops", displayName: "Operations" }]);
    listChannelsForTeam.mockResolvedValueOnce([
      { id: "19:ops-announce@thread.tacv2", displayName: "Announcements" },
      { id: "19:ops-random@thread.tacv2", displayName: "Random" },
    ]);

    const [result] = await resolveMSTeamsChannelAllowlist({
      cfg: {},
      entries: ["Operations"],
    });

    expect(result).toEqual({
      input: "Operations",
      resolved: true,
      teamId: "guid-ops",
      teamName: "Operations",
    });
  });
});

describe("looksLikeMSTeamsTargetId", () => {
  // Regression suite for https://github.com/openclaw/openclaw/issues/58001:
  // cron announce delivery rejected valid Teams conversation ids because the
  // validator only matched the `conversation:`-prefixed and `@thread`-suffixed
  // forms. It must now accept every documented Bot Framework + Graph format.
  it.each([
    "conversation:19:abc@thread.tacv2",
    "conversation:a:1abc",
    "conversation:8:orgid:2d8c2d2c-1111-2222-3333-444444444444",
  ])("accepts conversation-prefixed ids (%s)", (raw) => {
    expect(looksLikeMSTeamsTargetId(raw)).toBe(true);
  });

  it.each(["19:AdviChannelId@thread.tacv2""19:abc@thread.tacv2""19:abc@thread.skype"])(
    "accepts bare channel/group conversation ids (%s)",
    (raw) => {
      expect(looksLikeMSTeamsTargetId(raw)).toBe(true);
    },
  );

  it("accepts the Graph 1:1 chat thread format", () => {
    expect(
      looksLikeMSTeamsTargetId(
        "19:40a1a0ed4ff24164a21955518990c197_2d8c2d2c11112222@unq.gbl.spaces",
      ),
    ).toBe(true);
  });

  it.each(["a:1abc123def""a:1xyz-abc_def""A:1UPPER"])(
    "accepts Bot Framework personal chat ids (%s)",
    (raw) => {
      expect(looksLikeMSTeamsTargetId(raw)).toBe(true);
    },
  );

  it.each(["8:orgid:2d8c2d2c-1111-2222-3333-444444444444""8:orgid:user-object-id"])(
    "accepts Bot Framework org-scoped personal chat ids (%s)",
    (raw) => {
      expect(looksLikeMSTeamsTargetId(raw)).toBe(true);
    },
  );

  it("accepts Bot Framework user ids", () => {
    expect(looksLikeMSTeamsTargetId("29:1a2b3c4d5e6f")).toBe(true);
  });

  it("accepts user:<aad-object-id> ids", () => {
    expect(looksLikeMSTeamsTargetId("user:40a1a0ed-4ff2-4164-a219-55518990c197")).toBe(true);
  });

  it.each(["""   ""user:John Smith""Product Team/Roadmap""Engineering""hello"])(
    "rejects non-id inputs (%s)",
    (raw) => {
      expect(looksLikeMSTeamsTargetId(raw)).toBe(false);
    },
  );

  it("normalizes leading/trailing whitespace before classifying", () => {
    expect(looksLikeMSTeamsTargetId("  19:abc@thread.tacv2  ")).toBe(true);
  });
});

Messung V0.5 in Prozent
C=97 H=98 G=97

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-05-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.