惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Jina AI
Jina AI
Hugging Face - Blog
Hugging Face - Blog
博客园 - 三生石上(FineUI控件)
博客园 - 【当耐特】
大猫的无限游戏
大猫的无限游戏
IT之家
IT之家
宝玉的分享
宝玉的分享
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
阮一峰的网络日志
阮一峰的网络日志
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
爱范儿
爱范儿
小众软件
小众软件
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
S
SegmentFault 最新的问题
博客园 - Franky
博客园_首页
T
Tailwind CSS Blog
雷峰网
雷峰网
罗磊的独立博客

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
feat(plugin-state): add registerIfAbsent keyed store (#77...
amknight · 2026-05-04 · via Recent Commits to openclaw:main

@@ -58,6 +58,145 @@ describe("plugin state keyed store", () => {

5858

});

5959

});

606061+

it("registerIfAbsent inserts the first value and preserves live duplicates", async () => {

62+

await withOpenClawTestState({ label: "plugin-state-register-if-absent-live" }, async () => {

63+

vi.useFakeTimers();

64+

const store = createPluginStateKeyedStore<{ version: number }>("discord", {

65+

namespace: "claims",

66+

maxEntries: 10,

67+

});

68+69+

vi.setSystemTime(1000);

70+

await expect(store.registerIfAbsent("claim", { version: 1 }, { ttlMs: 1000 })).resolves.toBe(

71+

true,

72+

);

73+

vi.setSystemTime(1200);

74+

await expect(store.registerIfAbsent("claim", { version: 2 }, { ttlMs: 5000 })).resolves.toBe(

75+

false,

76+

);

77+78+

await expect(store.lookup("claim")).resolves.toEqual({ version: 1 });

79+

await expect(store.entries()).resolves.toMatchObject([

80+

{ key: "claim", value: { version: 1 }, createdAt: 1000, expiresAt: 2000 },

81+

]);

82+

});

83+

});

84+85+

it("registerIfAbsent replaces expired keys", async () => {

86+

await withOpenClawTestState({ label: "plugin-state-register-if-absent-expired" }, async () => {

87+

vi.useFakeTimers();

88+

const store = createPluginStateKeyedStore<{ version: number }>("discord", {

89+

namespace: "claims-expired",

90+

maxEntries: 10,

91+

});

92+93+

vi.setSystemTime(1000);

94+

await expect(store.registerIfAbsent("claim", { version: 1 }, { ttlMs: 100 })).resolves.toBe(

95+

true,

96+

);

97+

vi.setSystemTime(1200);

98+

await expect(store.registerIfAbsent("claim", { version: 2 })).resolves.toBe(true);

99+100+

await expect(store.lookup("claim")).resolves.toEqual({ version: 2 });

101+

await expect(store.entries()).resolves.toMatchObject([

102+

{ key: "claim", value: { version: 2 }, createdAt: 1200 },

103+

]);

104+

});

105+

});

106+107+

it("registerIfAbsent keeps plugin and namespace claims isolated", async () => {

108+

await withOpenClawTestState(

109+

{ label: "plugin-state-register-if-absent-isolation" },

110+

async () => {

111+

const discordA = createPluginStateKeyedStore<{ owner: string }>("discord", {

112+

namespace: "claims-a",

113+

maxEntries: 10,

114+

});

115+

const discordB = createPluginStateKeyedStore<{ owner: string }>("discord", {

116+

namespace: "claims-b",

117+

maxEntries: 10,

118+

});

119+

const telegramA = createPluginStateKeyedStore<{ owner: string }>("telegram", {

120+

namespace: "claims-a",

121+

maxEntries: 10,

122+

});

123+124+

await expect(discordA.registerIfAbsent("same", { owner: "discord-a" })).resolves.toBe(true);

125+

await expect(discordB.registerIfAbsent("same", { owner: "discord-b" })).resolves.toBe(true);

126+

await expect(telegramA.registerIfAbsent("same", { owner: "telegram-a" })).resolves.toBe(

127+

true,

128+

);

129+

await expect(discordA.registerIfAbsent("same", { owner: "overwrite" })).resolves.toBe(

130+

false,

131+

);

132+133+

await expect(discordA.lookup("same")).resolves.toEqual({ owner: "discord-a" });

134+

await expect(discordB.lookup("same")).resolves.toEqual({ owner: "discord-b" });

135+

await expect(telegramA.lookup("same")).resolves.toEqual({ owner: "telegram-a" });

136+

},

137+

);

138+

});

139+140+

it("registerIfAbsent only lets one parallel claimant win", async () => {

141+

await withOpenClawTestState({ label: "plugin-state-register-if-absent-race" }, async () => {

142+

const store = createPluginStateKeyedStore<{ claimant: number }>("discord", {

143+

namespace: "claims-race",

144+

maxEntries: 10,

145+

});

146+147+

const attempts = await Promise.all(

148+

Array.from({ length: 25 }, async (_, claimant) =>

149+

store.registerIfAbsent("claim", { claimant }),

150+

),

151+

);

152+153+

expect(attempts.filter(Boolean)).toHaveLength(1);

154+

const stored = await store.lookup("claim");

155+

expect(stored).toBeDefined();

156+

expect(attempts[stored?.claimant ?? -1]).toBe(true);

157+

});

158+

});

159+160+

it("registerIfAbsent preserves eviction and plugin row cap behavior", async () => {

161+

await withOpenClawTestState({ label: "plugin-state-register-if-absent-limits" }, async () => {

162+

vi.useFakeTimers();

163+

const evicting = createPluginStateKeyedStore<number>("discord", {

164+

namespace: "claims-evict",

165+

maxEntries: 2,

166+

});

167+

vi.setSystemTime(1000);

168+

await evicting.registerIfAbsent("a", 1);

169+

vi.setSystemTime(2000);

170+

await evicting.registerIfAbsent("b", 2);

171+

vi.setSystemTime(3000);

172+

await evicting.registerIfAbsent("c", 3);

173+

await expect(evicting.entries()).resolves.toMatchObject([{ key: "b" }, { key: "c" }]);

174+175+

seedPluginStateEntriesForTests([

176+

...Array.from({ length: 999 }, (_, entryIndex) => ({

177+

pluginId: "limited-plugin",

178+

namespace: "limit",

179+

key: `k-${entryIndex}`,

180+

value: { entryIndex },

181+

})),

182+

{

183+

pluginId: "limited-plugin",

184+

namespace: "sibling",

185+

key: "k-0",

186+

value: { sibling: true },

187+

},

188+

]);

189+

const limited = createPluginStateKeyedStore("limited-plugin", {

190+

namespace: "limit",

191+

maxEntries: 1_001,

192+

});

193+

await expect(limited.registerIfAbsent("overflow", { overflow: true })).rejects.toMatchObject({

194+

code: "PLUGIN_STATE_LIMIT_EXCEEDED",

195+

});

196+

await expect(limited.lookup("overflow")).resolves.toBeUndefined();

197+

});

198+

});

199+61200

it("returns undefined for missing lookups and consumes by deleting atomically", async () => {

62201

await withOpenClawTestState({ label: "plugin-state-consume" }, async () => {

63202

const store = createPluginStateKeyedStore<{ ok: boolean }>("discord", {