Files
openclaw/extensions/irc/doctor-contract-api.test.ts
Peter Steinberger 4a05a087a1 refactor(config): retire flat streaming keys from the last six channel schemas via doctor migration (#105709)
* refactor(config): retire flat streaming keys from the last six channel schemas

signal, irc, nextcloud-talk, whatsapp, googlechat, and mattermost now accept
only the nested streaming.{chunkMode,block.enabled,block.coalesce} shape
(mattermost also drops scalar/boolean streaming); flat spellings migrate via
each channel's defineChannelAliasMigration doctor contract with root seeding
for their wholesale-replace account merges.

* feat(channels): warn once per key when the deprecated flat streaming fallback is used

Bundled schemas now reject the flat delivery keys, so the streaming.ts
fallback only serves external SDK plugin configs; emit a once-per-process
per-key subsystem warning and pin the removal plan to the next release train.

* chore(config): regenerate bundled channel config metadata for nested-only streaming

* docs: describe nested-only channel streaming config and the SDK flat-key deprecation window

* fix(whatsapp): seed migrated named-account streaming from the accounts.default layer

WhatsApp resolution layers accounts.default shared config between root and
named accounts, so doctor-materialized account streaming objects now inherit
default-account settings over root ones; mattermost schema test moves to the
nested-only shape with explicit rejection coverage; scope flat-key deprecation
notes to the pending Matrix/Feishu migrations.

* fix(whatsapp): resolve the default account case-insensitively when seeding migrated streaming

* chore(plugin-sdk): repin API baseline for nested-only channel streaming types
2026-07-12 15:27:28 -07:00

62 lines
2.5 KiB
TypeScript

// Irc tests cover doctor contract api plugin behavior.
import { expectDefined } from "@openclaw/normalization-core";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { describe, expect, it } from "vitest";
import { legacyConfigRules, normalizeCompatibilityConfig } from "./doctor-contract-api.js";
function ircConfig(entry: Record<string, unknown>): OpenClawConfig {
return { channels: { irc: entry } } as never;
}
describe("irc streaming legacy config rules", () => {
const rootRule = legacyConfigRules.find((rule) => rule.path.join(".") === "channels.irc");
const accountRule = legacyConfigRules.find(
(rule) => rule.path.join(".") === "channels.irc.accounts",
);
it("matches flat delivery aliases at root and account level", () => {
expect(rootRule?.match?.({ blockStreaming: false }, {})).toBe(true);
expect(rootRule?.match?.({ streaming: { block: { enabled: false } } }, {})).toBe(false);
expect(accountRule?.match?.({ libera: { chunkMode: "newline" } }, {})).toBe(true);
expect(accountRule?.match?.({ libera: { streaming: { chunkMode: "newline" } } }, {})).toBe(
false,
);
});
});
describe("irc normalizeCompatibilityConfig streaming aliases", () => {
it("moves flat delivery aliases and seeds materialized account objects from root", () => {
const result = normalizeCompatibilityConfig({
cfg: ircConfig({
blockStreaming: true,
accounts: {
libera: { chunkMode: "newline" },
},
}),
});
const irc = result.config.channels?.irc as unknown as Record<string, unknown>;
expect(irc.streaming).toEqual({ block: { enabled: true } });
expect(irc.blockStreaming).toBeUndefined();
const libera = expectDefined(
(irc.accounts as Record<string, Record<string, unknown>>).libera,
"libera irc account",
);
// IRC's account merge replaces the root streaming object wholesale, so the
// migrated account object must carry the inherited root block settings.
expect(libera.streaming).toEqual({ chunkMode: "newline", block: { enabled: true } });
expect(libera.chunkMode).toBeUndefined();
});
it("is idempotent: a second run reports no changes", () => {
const first = normalizeCompatibilityConfig({
cfg: ircConfig({ chunkMode: "length", blockStreamingCoalesce: { minChars: 10 } }),
});
expect(first.changes.length).toBeGreaterThan(0);
const second = normalizeCompatibilityConfig({ cfg: first.config });
expect(second.changes).toEqual([]);
expect(second.config).toBe(first.config);
});
});