@alumbwe/anvil 1.0.42 → 1.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/anvil.js +18 -0
- package/package.json +13 -6
- package/src/agents/bundled-agents.generated.ts +1 -1
- package/src/index.tsx +27 -4
- package/src/login/plain-login.ts +60 -0
- package/src/login/supabase-auth.ts +346 -0
- package/src/utils/anvil-client.ts +4 -0
- package/src/utils/auth.ts +16 -4
- package/src/utils/config-dir.ts +1 -1
- package/vendor/@anvil/common/src/constants/deepseek-direct.ts +30 -1
- package/vendor/@anvil/common/src/util/project-ignore.ts +4 -3
- package/vendor/@anvil/sdk/dist/index.cjs +46 -10
- package/vendor/@anvil/sdk/dist/index.mjs +46 -10
- package/src/commands/__tests__/anvil-command-aliases.test.ts +0 -56
- package/src/commands/__tests__/bash-command.test.ts +0 -445
- package/src/commands/__tests__/command-args.test.ts +0 -349
- package/src/commands/__tests__/copy-conversation-clipboard.test.ts +0 -108
- package/src/commands/__tests__/copy-conversation.test.ts +0 -226
- package/src/commands/__tests__/diagnostics-command.test.ts +0 -35
- package/src/commands/__tests__/image.test.ts +0 -99
- package/src/commands/__tests__/init.test.ts +0 -456
- package/src/commands/__tests__/new-command-rotates-chat.test.ts +0 -91
- package/src/commands/__tests__/process-diagnostics.test.ts +0 -54
- package/src/commands/__tests__/prompt-builders.test.ts +0 -28
- package/src/commands/__tests__/router-input.test.ts +0 -298
- package/src/components/__tests__/ad-banner.test.tsx +0 -83
- package/src/components/__tests__/anvil-landing-heading-row.test.tsx +0 -91
- package/src/components/__tests__/anvil-model-selector.test.tsx +0 -519
- package/src/components/__tests__/anvil-offer-invariants.test.ts +0 -54
- package/src/components/__tests__/anvil-takeover-prompt.test.tsx +0 -212
- package/src/components/__tests__/chat-input-bar.test.tsx +0 -130
- package/src/components/__tests__/grid-layout.integration.test.tsx +0 -304
- package/src/components/__tests__/grid-layout.test.tsx +0 -1051
- package/src/components/__tests__/message-block.completion.test.tsx +0 -104
- package/src/components/__tests__/message-block.streaming.test.tsx +0 -78
- package/src/components/__tests__/message-with-agents.test.tsx +0 -579
- package/src/components/__tests__/multiline-input.test.tsx +0 -1132
- package/src/components/__tests__/queue-panel.test.tsx +0 -398
- package/src/components/__tests__/selectable-list.test.ts +0 -232
- package/src/components/__tests__/status-bar.test.tsx +0 -44
- package/src/components/__tests__/status-indicator.test.tsx +0 -204
- package/src/components/__tests__/terminal-thinking-orb.test.tsx +0 -111
- package/src/components/__tests__/user-error-banner.test.tsx +0 -102
- package/src/components/ask-user/__tests__/multiple-choice-form.test.ts +0 -432
- package/src/components/ask-user/__tests__/validation.test.ts +0 -213
- package/src/components/tools/__tests__/apply-patch.test.tsx +0 -84
- package/src/components/tools/__tests__/code-search.test.tsx +0 -45
- package/src/components/tools/__tests__/gravity-index.test.ts +0 -76
- package/src/components/tools/__tests__/render-ui.test.tsx +0 -92
- package/src/components/tools/__tests__/run-terminal-command.test.ts +0 -233
- package/src/contexts/__tests__/chat-runtime-context.test.tsx +0 -149
- package/src/data/slash-commands.test.ts +0 -24
- package/src/hooks/__tests__/holds-live-anvil-slot.test.ts +0 -49
- package/src/hooks/__tests__/session-fetch-signal.test.ts +0 -63
- package/src/hooks/__tests__/use-activity-query.test.ts +0 -1000
- package/src/hooks/__tests__/use-ask-user-bridge.test.ts +0 -176
- package/src/hooks/__tests__/use-auth-query.test.ts +0 -21
- package/src/hooks/__tests__/use-chat-input-remount.test.tsx +0 -66
- package/src/hooks/__tests__/use-clipboard.test.tsx +0 -73
- package/src/hooks/__tests__/use-connection-status.test.ts +0 -112
- package/src/hooks/__tests__/use-directory-browser.test.ts +0 -203
- package/src/hooks/__tests__/use-gravity-ad.test.ts +0 -73
- package/src/hooks/__tests__/use-grid-layout.test.ts +0 -349
- package/src/hooks/__tests__/use-input-history.test.ts +0 -699
- package/src/hooks/__tests__/use-message-queue-editing.test.tsx +0 -209
- package/src/hooks/__tests__/use-message-queue.test.tsx +0 -71
- package/src/hooks/__tests__/use-path-tab-completion.test.ts +0 -315
- package/src/hooks/__tests__/use-queue-controls.test.ts +0 -68
- package/src/hooks/__tests__/use-queue-ui.test.tsx +0 -83
- package/src/hooks/__tests__/use-searchable-list.test.ts +0 -359
- package/src/hooks/__tests__/use-send-message-timer.test.ts +0 -146
- package/src/hooks/__tests__/use-suggestion-engine-mention.test.ts +0 -361
- package/src/hooks/__tests__/use-suggestion-engine.test.ts +0 -485
- package/src/hooks/__tests__/use-terminal-focus.test.ts +0 -66
- package/src/hooks/__tests__/use-terminal-layout.test.ts +0 -675
- package/src/hooks/__tests__/use-timeout.test.ts +0 -330
- package/src/hooks/__tests__/use-usage-query.test.ts +0 -502
- package/src/hooks/__tests__/use-user-details-query.test.ts +0 -195
- package/src/hooks/helpers/__tests__/send-message.test.ts +0 -1918
- package/src/init/__tests__/init-direnv.test.ts +0 -526
- package/src/state/__tests__/chat-store-focus.test.ts +0 -22
- package/src/state/__tests__/feedback-store.test.ts +0 -257
- package/src/utils/__tests__/active-run.test.ts +0 -140
- package/src/utils/__tests__/activity-tracker.test.ts +0 -440
- package/src/utils/__tests__/agent-display.test.ts +0 -139
- package/src/utils/__tests__/analytics-client.test.ts +0 -262
- package/src/utils/__tests__/anonymous-id.test.ts +0 -74
- package/src/utils/__tests__/anvil-api.test.ts +0 -578
- package/src/utils/__tests__/anvil-instance-owner.test.ts +0 -69
- package/src/utils/__tests__/anvil-model-navigation.test.ts +0 -102
- package/src/utils/__tests__/anvil-premium-reset.test.ts +0 -79
- package/src/utils/__tests__/anvil-referral-cache.test.ts +0 -116
- package/src/utils/__tests__/anvil-session-api.test.ts +0 -215
- package/src/utils/__tests__/anvil-session-display.test.ts +0 -23
- package/src/utils/__tests__/anvil-streak-line.test.ts +0 -217
- package/src/utils/__tests__/arrays.test.ts +0 -264
- package/src/utils/__tests__/bash-context-processor.test.ts +0 -38
- package/src/utils/__tests__/block-processor.test.ts +0 -810
- package/src/utils/__tests__/chat-history.test.ts +0 -203
- package/src/utils/__tests__/chat-input-key-intercept.test.ts +0 -40
- package/src/utils/__tests__/chat-meta.test.ts +0 -130
- package/src/utils/__tests__/clipboard-linux.test.ts +0 -359
- package/src/utils/__tests__/clipboard.test.ts +0 -910
- package/src/utils/__tests__/code-search-summary.test.ts +0 -84
- package/src/utils/__tests__/collapse-helpers.test.ts +0 -1109
- package/src/utils/__tests__/error-handling.test.ts +0 -658
- package/src/utils/__tests__/exit-cleanly.test.ts +0 -110
- package/src/utils/__tests__/feedback-helpers.test.ts +0 -444
- package/src/utils/__tests__/feedback-submission.test.ts +0 -26
- package/src/utils/__tests__/fetch-usage.test.ts +0 -355
- package/src/utils/__tests__/fingerprint.test.ts +0 -144
- package/src/utils/__tests__/fixtures/terminal-command-broker-child.ts +0 -7
- package/src/utils/__tests__/fixtures/terminal-command-broker-entry.ts +0 -3
- package/src/utils/__tests__/fixtures/terminal-command-broker-owner.ts +0 -33
- package/src/utils/__tests__/format-elapsed-time.test.ts +0 -103
- package/src/utils/__tests__/format-timeout.test.ts +0 -87
- package/src/utils/__tests__/image-dimensions.test.ts +0 -249
- package/src/utils/__tests__/image-processor.test.ts +0 -209
- package/src/utils/__tests__/implementor-helpers.test.ts +0 -1458
- package/src/utils/__tests__/keyboard-actions.test.ts +0 -660
- package/src/utils/__tests__/layout-helpers.test.ts +0 -36
- package/src/utils/__tests__/lazy-response-ads.test.ts +0 -167
- package/src/utils/__tests__/log-shipper.test.ts +0 -53
- package/src/utils/__tests__/markdown-renderer.test.tsx +0 -421
- package/src/utils/__tests__/message-block-helpers.test.ts +0 -1263
- package/src/utils/__tests__/message-updater.test.ts +0 -645
- package/src/utils/__tests__/osc-timeout-scenarios.test.ts +0 -172
- package/src/utils/__tests__/pending-attachments.test.ts +0 -277
- package/src/utils/__tests__/polling-backoff.test.ts +0 -51
- package/src/utils/__tests__/queue-panel-actions.test.ts +0 -148
- package/src/utils/__tests__/run-state-storage.test.ts +0 -919
- package/src/utils/__tests__/safe-json.test.ts +0 -77
- package/src/utils/__tests__/sdk-event-handlers.test.ts +0 -685
- package/src/utils/__tests__/send-message-helpers.test.ts +0 -1898
- package/src/utils/__tests__/send-message-timer.test.ts +0 -64
- package/src/utils/__tests__/settings.test.ts +0 -99
- package/src/utils/__tests__/strings.test.ts +0 -192
- package/src/utils/__tests__/terminal-color-detection.test.ts +0 -425
- package/src/utils/__tests__/terminal-command-broker.test.ts +0 -528
- package/src/utils/__tests__/terminal-enter-detection.test.ts +0 -77
- package/src/utils/__tests__/terminal-io.test.ts +0 -137
- package/src/utils/__tests__/terminal-protocol-controller.test.ts +0 -119
- package/src/utils/__tests__/text-layout.test.ts +0 -82
- package/src/utils/__tests__/theme-platform-detection.test.ts +0 -125
- package/src/utils/__tests__/think-tag-parser.test.ts +0 -159
- package/src/utils/__tests__/trace-writer.test.ts +0 -223
- package/src/utils/__tests__/trim-chat-logs.test.ts +0 -78
- package/src/utils/__tests__/usage-banner-state.test.ts +0 -298
- package/src/utils/__tests__/validation-error-formatting.test.ts +0 -126
- package/src/utils/__tests__/windows-terminal-health.test.ts +0 -101
- package/src/utils/__tests__/write-file-atomic.test.ts +0 -127
- package/vendor/@anvil/common/src/__tests__/agent-validation.test.ts +0 -846
- package/vendor/@anvil/common/src/__tests__/anvil-model-availability.test.ts +0 -101
- package/vendor/@anvil/common/src/__tests__/anvil-models.test.ts +0 -1408
- package/vendor/@anvil/common/src/__tests__/anvil-public-data-use-copy.test.ts +0 -258
- package/vendor/@anvil/common/src/__tests__/anvil-referral-tiers.test.ts +0 -48
- package/vendor/@anvil/common/src/__tests__/anvil-spend-ceilings.test.ts +0 -282
- package/vendor/@anvil/common/src/__tests__/anvil-trust.test.ts +0 -526
- package/vendor/@anvil/common/src/__tests__/deepseek-direct.test.ts +0 -68
- package/vendor/@anvil/common/src/__tests__/disposable-email.test.ts +0 -132
- package/vendor/@anvil/common/src/__tests__/dynamic-agent-template-schema.test.ts +0 -422
- package/vendor/@anvil/common/src/__tests__/env-ci.test.ts +0 -167
- package/vendor/@anvil/common/src/__tests__/env-process.test.ts +0 -145
- package/vendor/@anvil/common/src/__tests__/foreign-client-shipped-agents.test.ts +0 -234
- package/vendor/@anvil/common/src/__tests__/foreign-client-signals.test.ts +0 -379
- package/vendor/@anvil/common/src/__tests__/free-agents.test.ts +0 -807
- package/vendor/@anvil/common/src/__tests__/gravity-capi.test.ts +0 -154
- package/vendor/@anvil/common/src/__tests__/handlesteps-parsing.test.ts +0 -246
- package/vendor/@anvil/common/src/__tests__/kimi-k3-god-only.test.ts +0 -77
- package/vendor/@anvil/common/src/__tests__/model-config.test.ts +0 -89
- package/vendor/@anvil/common/src/__tests__/project-file-tree.test.ts +0 -298
- package/vendor/@anvil/common/src/__tests__/provisioned-model-tiers.test.ts +0 -116
- package/vendor/@anvil/common/src/__tests__/reasoning-effort.test.ts +0 -167
- package/vendor/@anvil/common/src/__tests__/reddit-capi.test.ts +0 -132
- package/vendor/@anvil/common/src/__tests__/response-ad-positions.test.ts +0 -72
- package/vendor/@anvil/common/src/__tests__/user-state.test.ts +0 -30
- package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding-gate.test.ts +0 -63
- package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding.test.ts +0 -246
- package/vendor/@anvil/common/src/constants/__tests__/cf-worker-signals.test.ts +0 -106
- package/vendor/@anvil/common/src/testing/TESTING_PATTERNS.md +0 -351
- package/vendor/@anvil/common/src/testing/anvil-offer-invariants.ts +0 -169
- package/vendor/@anvil/common/src/testing/errors.ts +0 -33
- package/vendor/@anvil/common/src/testing/fixtures/agent-runtime.ts +0 -334
- package/vendor/@anvil/common/src/testing/impl/agent-runtime.ts +0 -6
- package/vendor/@anvil/common/src/testing/index.ts +0 -84
- package/vendor/@anvil/common/src/testing/mock-modules.ts +0 -53
- package/vendor/@anvil/common/src/testing/mock-types.ts +0 -123
- package/vendor/@anvil/common/src/testing/mocks/analytics.ts +0 -261
- package/vendor/@anvil/common/src/testing/mocks/child-process.ts +0 -93
- package/vendor/@anvil/common/src/testing/mocks/crypto.ts +0 -218
- package/vendor/@anvil/common/src/testing/mocks/database.ts +0 -337
- package/vendor/@anvil/common/src/testing/mocks/fetch.ts +0 -219
- package/vendor/@anvil/common/src/testing/mocks/filesystem.ts +0 -166
- package/vendor/@anvil/common/src/testing/mocks/index.ts +0 -101
- package/vendor/@anvil/common/src/testing/mocks/logger.ts +0 -135
- package/vendor/@anvil/common/src/testing/mocks/stream.ts +0 -313
- package/vendor/@anvil/common/src/testing/mocks/timers.ts +0 -132
- package/vendor/@anvil/common/src/testing/mocks/tree-sitter.ts +0 -127
- package/vendor/@anvil/common/src/testing/setup.ts +0 -282
- package/vendor/@anvil/common/src/tools/__tests__/compile-tool-definitions.test.ts +0 -34
- package/vendor/@anvil/common/src/tools/params/__tests__/coerce-to-array.test.ts +0 -213
- package/vendor/@anvil/common/src/tools/params/__tests__/read-docs.test.ts +0 -47
- package/vendor/@anvil/common/src/tools/params/__tests__/render-ui.test.ts +0 -65
- package/vendor/@anvil/common/src/tools/params/tool/__tests__/run-terminal-command-timeout.test.ts +0 -49
- package/vendor/@anvil/common/src/types/__tests__/dynamic-agent-template.test.ts +0 -20
- package/vendor/@anvil/common/src/util/__tests__/ad-user-agent.test.ts +0 -26
- package/vendor/@anvil/common/src/util/__tests__/analytics-dispatcher.test.ts +0 -122
- package/vendor/@anvil/common/src/util/__tests__/analytics-log.test.ts +0 -102
- package/vendor/@anvil/common/src/util/__tests__/analytics-sampling.test.ts +0 -143
- package/vendor/@anvil/common/src/util/__tests__/anvil-streak.test.ts +0 -202
- package/vendor/@anvil/common/src/util/__tests__/anvil-usage-summary.test.ts +0 -193
- package/vendor/@anvil/common/src/util/__tests__/axiom-only-log.test.ts +0 -140
- package/vendor/@anvil/common/src/util/__tests__/client-user-agent.test.ts +0 -148
- package/vendor/@anvil/common/src/util/__tests__/engagement-tracker.test.ts +0 -115
- package/vendor/@anvil/common/src/util/__tests__/env-file-path.test.ts +0 -57
- package/vendor/@anvil/common/src/util/__tests__/error-abort.test.ts +0 -774
- package/vendor/@anvil/common/src/util/__tests__/error-api-details.test.ts +0 -206
- package/vendor/@anvil/common/src/util/__tests__/file-read-limits.test.ts +0 -173
- package/vendor/@anvil/common/src/util/__tests__/format-code-search.test.ts +0 -60
- package/vendor/@anvil/common/src/util/__tests__/log-mirror.test.ts +0 -31
- package/vendor/@anvil/common/src/util/__tests__/messages.test.ts +0 -1252
- package/vendor/@anvil/common/src/util/__tests__/partial-json-delta.test.ts +0 -505
- package/vendor/@anvil/common/src/util/__tests__/path.test.ts +0 -22
- package/vendor/@anvil/common/src/util/__tests__/project-ignore.test.ts +0 -90
- package/vendor/@anvil/common/src/util/__tests__/promise.test.ts +0 -325
- package/vendor/@anvil/common/src/util/__tests__/rate-limit.test.ts +0 -28
- package/vendor/@anvil/common/src/util/__tests__/reddit-anvil-retention.test.ts +0 -130
- package/vendor/@anvil/common/src/util/__tests__/saxy.test.ts +0 -1008
- package/vendor/@anvil/common/src/util/__tests__/split-data.test.ts +0 -289
- package/vendor/@anvil/common/src/util/__tests__/string.test.ts +0 -239
- package/vendor/@anvil/common/src/util/__tests__/thread-title.test.ts +0 -70
- package/vendor/@anvil/common/src/util/__tests__/tool-result-media-order.test.ts +0 -108
- package/vendor/@anvil/common/src/util/__tests__/ttft-histogram.test.ts +0 -180
- package/vendor/@anvil/common/src/util/__tests__/with-timeout.test.ts +0 -24
- package/vendor/@anvil/common/src/util/__tests__/zoned-time.test.ts +0 -88
- package/vendor/@anvil/common/src/util/skills.test.ts +0 -44
- package/vendor/@anvil/sdk/dist/index.cjs.map +0 -440
- package/vendor/@anvil/sdk/dist/index.mjs.map +0 -440
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach, afterEach, mock } from 'bun:test'
|
|
2
|
-
import * as fs from 'fs'
|
|
3
|
-
import * as os from 'os'
|
|
4
|
-
import * as path from 'path'
|
|
5
|
-
|
|
6
|
-
// Force tracing on so the test is deterministic
|
|
7
|
-
mock.module('@anvil/common/env', () => ({
|
|
8
|
-
IS_DEV: true,
|
|
9
|
-
}))
|
|
10
|
-
|
|
11
|
-
import { createTraceWriter } from '../trace-writer'
|
|
12
|
-
import { setProjectRoot, tryGetProjectRoot } from '../../project-files'
|
|
13
|
-
|
|
14
|
-
import type { Message } from '@anvil/common/types/messages/anvil-message'
|
|
15
|
-
|
|
16
|
-
let tempDir = ''
|
|
17
|
-
|
|
18
|
-
function tracePathFor(dir: string): string {
|
|
19
|
-
return path.join(dir, 'debug', 'trace.jsonl')
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function readTraceLines(): any[] {
|
|
23
|
-
const tracePath = tracePathFor(tempDir)
|
|
24
|
-
if (!fs.existsSync(tracePath)) return []
|
|
25
|
-
return fs
|
|
26
|
-
.readFileSync(tracePath, 'utf8')
|
|
27
|
-
.trim()
|
|
28
|
-
.split('\n')
|
|
29
|
-
.map((line) => JSON.parse(line))
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const userMessage = (text: string): Message => ({
|
|
33
|
-
role: 'user',
|
|
34
|
-
content: [{ type: 'text', text }],
|
|
35
|
-
})
|
|
36
|
-
const assistantMessage = (text: string): Message => ({
|
|
37
|
-
role: 'assistant',
|
|
38
|
-
content: [{ type: 'text', text }],
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
const baseParams = {
|
|
42
|
-
agentId: 'agent-1',
|
|
43
|
-
agentType: 'base',
|
|
44
|
-
runId: 'run-1',
|
|
45
|
-
userInputId: 'input-1',
|
|
46
|
-
system: undefined,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
describe('createTraceWriter', () => {
|
|
50
|
-
let originalProjectRoot: string | undefined
|
|
51
|
-
|
|
52
|
-
beforeEach(() => {
|
|
53
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'anvil-trace-'))
|
|
54
|
-
originalProjectRoot = tryGetProjectRoot()
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
afterEach(() => {
|
|
58
|
-
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
59
|
-
if (originalProjectRoot !== undefined) {
|
|
60
|
-
setProjectRoot(originalProjectRoot)
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
test('falls back to the real project-root-based path when no resolver is injected', () => {
|
|
65
|
-
setProjectRoot(tempDir)
|
|
66
|
-
const writer = createTraceWriter()!
|
|
67
|
-
|
|
68
|
-
writer.recordStep({
|
|
69
|
-
...baseParams,
|
|
70
|
-
step: 1,
|
|
71
|
-
messages: [userMessage('hello')],
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
const lines = readTraceLines()
|
|
75
|
-
expect(lines).toHaveLength(1)
|
|
76
|
-
expect(lines[0].message.role).toBe('user')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
test('writes each message exactly once across steps', () => {
|
|
80
|
-
const writer = createTraceWriter(() => tracePathFor(tempDir))!
|
|
81
|
-
const history = [userMessage('hello')]
|
|
82
|
-
|
|
83
|
-
writer.recordStep({ ...baseParams, step: 1, messages: history })
|
|
84
|
-
history.push(assistantMessage('hi there'))
|
|
85
|
-
writer.recordStep({ ...baseParams, step: 1, messages: history })
|
|
86
|
-
// Re-recording the same history adds nothing
|
|
87
|
-
writer.recordStep({ ...baseParams, step: 2, messages: history })
|
|
88
|
-
|
|
89
|
-
const lines = readTraceLines()
|
|
90
|
-
expect(lines).toHaveLength(2)
|
|
91
|
-
expect(lines.map((l) => l.message.role)).toEqual(['user', 'assistant'])
|
|
92
|
-
expect(lines.map((l) => l.index)).toEqual([0, 1])
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
test('records system prompt once and again only when it changes', () => {
|
|
96
|
-
const writer = createTraceWriter(() => tracePathFor(tempDir))!
|
|
97
|
-
|
|
98
|
-
writer.recordStep({
|
|
99
|
-
...baseParams,
|
|
100
|
-
step: 1,
|
|
101
|
-
system: 'be helpful',
|
|
102
|
-
messages: [userMessage('hello')],
|
|
103
|
-
})
|
|
104
|
-
writer.recordStep({
|
|
105
|
-
...baseParams,
|
|
106
|
-
step: 2,
|
|
107
|
-
system: 'be helpful',
|
|
108
|
-
messages: [userMessage('hello'), assistantMessage('hi')],
|
|
109
|
-
})
|
|
110
|
-
writer.recordStep({
|
|
111
|
-
...baseParams,
|
|
112
|
-
step: 3,
|
|
113
|
-
system: 'be terse',
|
|
114
|
-
messages: [
|
|
115
|
-
userMessage('hello'),
|
|
116
|
-
assistantMessage('hi'),
|
|
117
|
-
userMessage('more'),
|
|
118
|
-
],
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
const systemLines = readTraceLines().filter((l) => l.type === 'system')
|
|
122
|
-
expect(systemLines.map((l) => l.system)).toEqual([
|
|
123
|
-
'be helpful',
|
|
124
|
-
'be terse',
|
|
125
|
-
])
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
test('detects history rewrites and re-dumps the new history', () => {
|
|
129
|
-
const writer = createTraceWriter(() => tracePathFor(tempDir))!
|
|
130
|
-
|
|
131
|
-
writer.recordStep({
|
|
132
|
-
...baseParams,
|
|
133
|
-
step: 1,
|
|
134
|
-
messages: [
|
|
135
|
-
userMessage('a'),
|
|
136
|
-
assistantMessage('b'),
|
|
137
|
-
userMessage('c'),
|
|
138
|
-
],
|
|
139
|
-
})
|
|
140
|
-
// Compaction: history replaced with a shorter one
|
|
141
|
-
writer.recordStep({
|
|
142
|
-
...baseParams,
|
|
143
|
-
step: 2,
|
|
144
|
-
messages: [userMessage('summary of earlier conversation')],
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
const lines = readTraceLines()
|
|
148
|
-
const marker = lines.find((l) => l.type === 'history_rewritten')
|
|
149
|
-
expect(marker).toBeDefined()
|
|
150
|
-
expect(marker.previousMessageCount).toBe(3)
|
|
151
|
-
expect(marker.messageCount).toBe(1)
|
|
152
|
-
// 3 original + 1 re-dumped after the marker
|
|
153
|
-
expect(lines.filter((l) => l.type === 'message')).toHaveLength(4)
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
test('tracks agents independently', () => {
|
|
157
|
-
const writer = createTraceWriter(() => tracePathFor(tempDir))!
|
|
158
|
-
|
|
159
|
-
writer.recordStep({
|
|
160
|
-
...baseParams,
|
|
161
|
-
step: 1,
|
|
162
|
-
messages: [userMessage('main')],
|
|
163
|
-
})
|
|
164
|
-
writer.recordStep({
|
|
165
|
-
...baseParams,
|
|
166
|
-
agentId: 'agent-2',
|
|
167
|
-
agentType: 'researcher',
|
|
168
|
-
step: 1,
|
|
169
|
-
messages: [userMessage('sub')],
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
const lines = readTraceLines()
|
|
173
|
-
expect(lines.filter((l) => l.type === 'history_rewritten')).toHaveLength(0)
|
|
174
|
-
expect(lines.map((l) => l.agentId)).toEqual(['agent-1', 'agent-2'])
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
test('writes base64 image data in full', () => {
|
|
178
|
-
const base64 = 'a'.repeat(10_000)
|
|
179
|
-
const writer = createTraceWriter(() => tracePathFor(tempDir))!
|
|
180
|
-
|
|
181
|
-
writer.recordStep({
|
|
182
|
-
...baseParams,
|
|
183
|
-
step: 1,
|
|
184
|
-
messages: [
|
|
185
|
-
{
|
|
186
|
-
role: 'user',
|
|
187
|
-
content: [
|
|
188
|
-
{ type: 'text', text: 'look at this' },
|
|
189
|
-
{ type: 'image', image: base64, mediaType: 'image/png' },
|
|
190
|
-
],
|
|
191
|
-
} as Message,
|
|
192
|
-
],
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
const lines = readTraceLines()
|
|
196
|
-
expect(lines).toHaveLength(1)
|
|
197
|
-
expect(lines[0].message.content[1].image).toBe(base64)
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
test('breaks cycles in traced message content instead of throwing', () => {
|
|
201
|
-
const writer = createTraceWriter(() => tracePathFor(tempDir))!
|
|
202
|
-
const cyclic: any = { type: 'text', text: 'boom' }
|
|
203
|
-
cyclic.self = cyclic
|
|
204
|
-
|
|
205
|
-
writer.recordStep({
|
|
206
|
-
...baseParams,
|
|
207
|
-
step: 1,
|
|
208
|
-
messages: [
|
|
209
|
-
{
|
|
210
|
-
role: 'user',
|
|
211
|
-
content: [{ type: 'text', text: 'plain' }, cyclic],
|
|
212
|
-
} as unknown as Message,
|
|
213
|
-
],
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
const lines = readTraceLines()
|
|
217
|
-
expect(lines).toHaveLength(1)
|
|
218
|
-
const message = lines[0].message
|
|
219
|
-
expect(message.content[0].text).toBe('plain')
|
|
220
|
-
expect(message.content[1].text).toBe('boom')
|
|
221
|
-
expect(message.content[1].self).toBe('[Circular]')
|
|
222
|
-
})
|
|
223
|
-
})
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach, afterEach, mock } from 'bun:test'
|
|
2
|
-
import * as fs from 'fs'
|
|
3
|
-
import * as os from 'os'
|
|
4
|
-
import * as path from 'path'
|
|
5
|
-
|
|
6
|
-
mock.module('../logger', () => ({
|
|
7
|
-
CHAT_LOG_FILENAME: 'log.jsonl',
|
|
8
|
-
logger: {
|
|
9
|
-
debug: () => {},
|
|
10
|
-
info: () => {},
|
|
11
|
-
warn: () => {},
|
|
12
|
-
error: () => {},
|
|
13
|
-
fatal: () => {},
|
|
14
|
-
},
|
|
15
|
-
}))
|
|
16
|
-
|
|
17
|
-
import { trimOversizedChatLogs } from '../chat-history'
|
|
18
|
-
|
|
19
|
-
let tempDataDir = ''
|
|
20
|
-
|
|
21
|
-
function writeLog(
|
|
22
|
-
chatId: string,
|
|
23
|
-
sizeBytes: number,
|
|
24
|
-
{ ageDays = 0 }: { ageDays?: number } = {},
|
|
25
|
-
) {
|
|
26
|
-
const chatDir = path.join(tempDataDir, 'chats', chatId)
|
|
27
|
-
fs.mkdirSync(chatDir, { recursive: true })
|
|
28
|
-
const logFile = path.join(chatDir, 'log.jsonl')
|
|
29
|
-
fs.writeFileSync(logFile, Buffer.alloc(sizeBytes, 'a'))
|
|
30
|
-
if (ageDays > 0) {
|
|
31
|
-
const mtime = new Date(Date.now() - ageDays * 24 * 60 * 60 * 1000)
|
|
32
|
-
fs.utimesSync(logFile, mtime, mtime)
|
|
33
|
-
}
|
|
34
|
-
return logFile
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
describe('trimOversizedChatLogs', () => {
|
|
38
|
-
beforeEach(() => {
|
|
39
|
-
tempDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'anvil-logs-'))
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
fs.rmSync(tempDataDir, { recursive: true, force: true })
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
test('deletes oversized old log files and keeps small ones', () => {
|
|
47
|
-
const bigOldLog = writeLog('chat-big', 11 * 1024 * 1024, { ageDays: 15 })
|
|
48
|
-
const smallOldLog = writeLog('chat-small', 1024, { ageDays: 15 })
|
|
49
|
-
const messagesFile = path.join(
|
|
50
|
-
tempDataDir,
|
|
51
|
-
'chats',
|
|
52
|
-
'chat-big',
|
|
53
|
-
'chat-messages.json',
|
|
54
|
-
)
|
|
55
|
-
fs.writeFileSync(messagesFile, '[]')
|
|
56
|
-
|
|
57
|
-
trimOversizedChatLogs(tempDataDir)
|
|
58
|
-
|
|
59
|
-
expect(fs.existsSync(bigOldLog)).toBe(false)
|
|
60
|
-
expect(fs.existsSync(smallOldLog)).toBe(true)
|
|
61
|
-
// Chat history files are never touched
|
|
62
|
-
expect(fs.existsSync(messagesFile)).toBe(true)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
test('keeps oversized log files from recent chats', () => {
|
|
66
|
-
const bigRecentLog = writeLog('chat-recent', 11 * 1024 * 1024, {
|
|
67
|
-
ageDays: 5,
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
trimOversizedChatLogs(tempDataDir)
|
|
71
|
-
|
|
72
|
-
expect(fs.existsSync(bigRecentLog)).toBe(true)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test('does nothing when chats directory does not exist', () => {
|
|
76
|
-
expect(() => trimOversizedChatLogs(tempDataDir)).not.toThrow()
|
|
77
|
-
})
|
|
78
|
-
})
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from 'bun:test'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
getBannerColorLevel,
|
|
5
|
-
getThresholdInfo,
|
|
6
|
-
generateLoadingBannerText,
|
|
7
|
-
shouldAutoShowBanner,
|
|
8
|
-
} from '../usage-banner-state'
|
|
9
|
-
|
|
10
|
-
describe('usage-banner-state', () => {
|
|
11
|
-
describe('getThresholdInfo', () => {
|
|
12
|
-
test('returns high tier for >= 1000 credits', () => {
|
|
13
|
-
expect(getThresholdInfo(1000)).toEqual({
|
|
14
|
-
tier: 'high',
|
|
15
|
-
colorLevel: 'success',
|
|
16
|
-
threshold: 1000,
|
|
17
|
-
})
|
|
18
|
-
expect(getThresholdInfo(5000)).toEqual({
|
|
19
|
-
tier: 'high',
|
|
20
|
-
colorLevel: 'success',
|
|
21
|
-
threshold: 1000,
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test('returns medium tier for 500-999 credits', () => {
|
|
26
|
-
expect(getThresholdInfo(999)).toEqual({
|
|
27
|
-
tier: 'medium',
|
|
28
|
-
colorLevel: 'warning',
|
|
29
|
-
threshold: 500,
|
|
30
|
-
})
|
|
31
|
-
expect(getThresholdInfo(500)).toEqual({
|
|
32
|
-
tier: 'medium',
|
|
33
|
-
colorLevel: 'warning',
|
|
34
|
-
threshold: 500,
|
|
35
|
-
})
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
test('returns low tier for 100-499 credits', () => {
|
|
39
|
-
expect(getThresholdInfo(499)).toEqual({
|
|
40
|
-
tier: 'low',
|
|
41
|
-
colorLevel: 'warning',
|
|
42
|
-
threshold: 100,
|
|
43
|
-
})
|
|
44
|
-
expect(getThresholdInfo(100)).toEqual({
|
|
45
|
-
tier: 'low',
|
|
46
|
-
colorLevel: 'warning',
|
|
47
|
-
threshold: 100,
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
test('returns out tier for < 100 credits', () => {
|
|
52
|
-
expect(getThresholdInfo(99)).toEqual({
|
|
53
|
-
tier: 'out',
|
|
54
|
-
colorLevel: 'error',
|
|
55
|
-
threshold: 0,
|
|
56
|
-
})
|
|
57
|
-
expect(getThresholdInfo(0)).toEqual({
|
|
58
|
-
tier: 'out',
|
|
59
|
-
colorLevel: 'error',
|
|
60
|
-
threshold: 0,
|
|
61
|
-
})
|
|
62
|
-
expect(getThresholdInfo(-50)).toEqual({
|
|
63
|
-
tier: 'out',
|
|
64
|
-
colorLevel: 'error',
|
|
65
|
-
threshold: 0,
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test('returns medium tier when balance is unknown', () => {
|
|
70
|
-
expect(getThresholdInfo(null)).toEqual({
|
|
71
|
-
tier: 'medium',
|
|
72
|
-
colorLevel: 'warning',
|
|
73
|
-
threshold: 500,
|
|
74
|
-
})
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
describe('getBannerColorLevel', () => {
|
|
79
|
-
test('shows success for healthy credit balance (>= 1000)', () => {
|
|
80
|
-
expect(getBannerColorLevel(1000)).toBe('success')
|
|
81
|
-
expect(getBannerColorLevel(5000)).toBe('success')
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test('shows warning for moderate credit balance (100-999)', () => {
|
|
85
|
-
expect(getBannerColorLevel(999)).toBe('warning')
|
|
86
|
-
expect(getBannerColorLevel(500)).toBe('warning')
|
|
87
|
-
expect(getBannerColorLevel(100)).toBe('warning')
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
test('shows error for low credit balance (< 100)', () => {
|
|
91
|
-
expect(getBannerColorLevel(99)).toBe('error')
|
|
92
|
-
expect(getBannerColorLevel(0)).toBe('error')
|
|
93
|
-
expect(getBannerColorLevel(-50)).toBe('error')
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
test('shows warning when balance is unknown', () => {
|
|
97
|
-
expect(getBannerColorLevel(null)).toBe('warning')
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
describe('generateLoadingBannerText', () => {
|
|
102
|
-
test('shows session usage while loading', () => {
|
|
103
|
-
const text = generateLoadingBannerText(150)
|
|
104
|
-
expect(text).toContain('150')
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
test('indicates loading state', () => {
|
|
108
|
-
const text = generateLoadingBannerText(0)
|
|
109
|
-
expect(text.toLowerCase()).toContain('loading')
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
describe('shouldAutoShowBanner', () => {
|
|
114
|
-
describe('when banner should NOT auto-show', () => {
|
|
115
|
-
test('during active AI response chain', () => {
|
|
116
|
-
const result = shouldAutoShowBanner(true, true, 50, null)
|
|
117
|
-
expect(result.shouldShow).toBe(false)
|
|
118
|
-
expect(result.newWarningThreshold).toBe(null)
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
test('when user is not authenticated', () => {
|
|
122
|
-
const result = shouldAutoShowBanner(false, false, 50, null)
|
|
123
|
-
expect(result.shouldShow).toBe(false)
|
|
124
|
-
expect(result.newWarningThreshold).toBe(null)
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
test('when balance data is unavailable', () => {
|
|
128
|
-
const result = shouldAutoShowBanner(false, true, null, null)
|
|
129
|
-
expect(result.shouldShow).toBe(false)
|
|
130
|
-
expect(result.newWarningThreshold).toBe(null)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
test('when user has healthy credits (>= 1000)', () => {
|
|
134
|
-
const result = shouldAutoShowBanner(false, true, 1500, null)
|
|
135
|
-
expect(result.shouldShow).toBe(false)
|
|
136
|
-
expect(result.newWarningThreshold).toBe(null)
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
test('when staying within the same threshold bucket', () => {
|
|
140
|
-
// Already warned about 500, current is 400 (still in < 500 bucket and > 100)
|
|
141
|
-
const result = shouldAutoShowBanner(false, true, 400, 500)
|
|
142
|
-
expect(result.shouldShow).toBe(false)
|
|
143
|
-
expect(result.newWarningThreshold).toBe(500)
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
describe('when banner SHOULD auto-show', () => {
|
|
148
|
-
test('when crossing HIGH threshold (< 1000)', () => {
|
|
149
|
-
const result = shouldAutoShowBanner(false, true, 999, null)
|
|
150
|
-
expect(result.shouldShow).toBe(true)
|
|
151
|
-
expect(result.newWarningThreshold).toBe(1000)
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
test('when crossing MEDIUM threshold (< 500)', () => {
|
|
155
|
-
const result = shouldAutoShowBanner(false, true, 499, null)
|
|
156
|
-
expect(result.shouldShow).toBe(true)
|
|
157
|
-
expect(result.newWarningThreshold).toBe(500)
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
test('when crossing LOW threshold (< 100)', () => {
|
|
161
|
-
const result = shouldAutoShowBanner(false, true, 99, null)
|
|
162
|
-
expect(result.shouldShow).toBe(true)
|
|
163
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
test('when crossing multiple thresholds at once (e.g. dropping huge amount)', () => {
|
|
167
|
-
// Dropping from >1000 to <100
|
|
168
|
-
const result = shouldAutoShowBanner(false, true, 50, null)
|
|
169
|
-
expect(result.shouldShow).toBe(true)
|
|
170
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
test('when crossing to a lower threshold than previously warned', () => {
|
|
174
|
-
// Previously warned at 500, now dropped below 100
|
|
175
|
-
const result = shouldAutoShowBanner(false, true, 50, 500)
|
|
176
|
-
expect(result.shouldShow).toBe(true)
|
|
177
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
178
|
-
})
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
describe('auto top-up enabled behavior', () => {
|
|
182
|
-
test('does not auto-show for auto-top-up users above 0 credits', () => {
|
|
183
|
-
// Even at low credits, auto-top-up users shouldn't see warnings
|
|
184
|
-
const result = shouldAutoShowBanner(false, true, 50, null, true)
|
|
185
|
-
expect(result.shouldShow).toBe(false)
|
|
186
|
-
expect(result.newWarningThreshold).toBe(null)
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
test('does not auto-show for auto-top-up users at any positive threshold', () => {
|
|
190
|
-
// At 500 credits - would normally warn
|
|
191
|
-
let result = shouldAutoShowBanner(false, true, 499, null, true)
|
|
192
|
-
expect(result.shouldShow).toBe(false)
|
|
193
|
-
|
|
194
|
-
// At 100 credits - would normally warn
|
|
195
|
-
result = shouldAutoShowBanner(false, true, 99, null, true)
|
|
196
|
-
expect(result.shouldShow).toBe(false)
|
|
197
|
-
|
|
198
|
-
// Even at 1 credit
|
|
199
|
-
result = shouldAutoShowBanner(false, true, 1, null, true)
|
|
200
|
-
expect(result.shouldShow).toBe(false)
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
test('DOES auto-show for auto-top-up users when truly out (0 credits)', () => {
|
|
204
|
-
const result = shouldAutoShowBanner(false, true, 0, null, true)
|
|
205
|
-
expect(result.shouldShow).toBe(true)
|
|
206
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
test('DOES auto-show for auto-top-up users when in debt (negative credits)', () => {
|
|
210
|
-
const result = shouldAutoShowBanner(false, true, -50, null, true)
|
|
211
|
-
expect(result.shouldShow).toBe(true)
|
|
212
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
test('non-auto-top-up users still get warnings as normal', () => {
|
|
216
|
-
// Without auto-top-up, should warn at low credits
|
|
217
|
-
const result = shouldAutoShowBanner(false, true, 50, null, false)
|
|
218
|
-
expect(result.shouldShow).toBe(true)
|
|
219
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
test('defaults autoTopUpEnabled to false when omitted', () => {
|
|
223
|
-
// When autoTopUpEnabled parameter is omitted, should behave like false
|
|
224
|
-
const result = shouldAutoShowBanner(false, true, 50, null)
|
|
225
|
-
expect(result.shouldShow).toBe(true)
|
|
226
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
227
|
-
})
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
describe('combined scenarios', () => {
|
|
231
|
-
test('chain in progress takes precedence over auto-top-up status', () => {
|
|
232
|
-
// Even with auto-top-up disabled and low credits, chain in progress blocks showing
|
|
233
|
-
const result = shouldAutoShowBanner(true, true, 50, null, false)
|
|
234
|
-
expect(result.shouldShow).toBe(false)
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
test('unauthenticated takes precedence over auto-top-up status', () => {
|
|
238
|
-
// Even with auto-top-up disabled and low credits, no auth token blocks showing
|
|
239
|
-
const result = shouldAutoShowBanner(false, false, 50, null, false)
|
|
240
|
-
expect(result.shouldShow).toBe(false)
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
test('null balance takes precedence over auto-top-up status', () => {
|
|
244
|
-
// Even with auto-top-up disabled, null balance blocks showing
|
|
245
|
-
const result = shouldAutoShowBanner(false, true, null, null, false)
|
|
246
|
-
expect(result.shouldShow).toBe(false)
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
test('auto-top-up user with previous warning threshold and now at 0 credits', () => {
|
|
250
|
-
// Auto-top-up user who was previously warned at 500, now at 0 - should show
|
|
251
|
-
const result = shouldAutoShowBanner(false, true, 0, 500, true)
|
|
252
|
-
expect(result.shouldShow).toBe(true)
|
|
253
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
test('auto-top-up user with healthy balance clears warning state', () => {
|
|
257
|
-
// Auto-top-up user who now has healthy balance should have cleared state
|
|
258
|
-
const result = shouldAutoShowBanner(false, true, 1500, 100, true)
|
|
259
|
-
expect(result.shouldShow).toBe(false)
|
|
260
|
-
expect(result.newWarningThreshold).toBe(null)
|
|
261
|
-
})
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
describe('state reset behavior', () => {
|
|
265
|
-
test('clears warning state when credits return to healthy', () => {
|
|
266
|
-
const result = shouldAutoShowBanner(false, true, 1500, 100)
|
|
267
|
-
expect(result.shouldShow).toBe(false)
|
|
268
|
-
expect(result.newWarningThreshold).toBe(null)
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
test('re-warns after refill and subsequent drop', () => {
|
|
272
|
-
// First: warned about low credits
|
|
273
|
-
let result = shouldAutoShowBanner(false, true, 50, null)
|
|
274
|
-
expect(result.shouldShow).toBe(true)
|
|
275
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
276
|
-
|
|
277
|
-
// Then: refilled
|
|
278
|
-
result = shouldAutoShowBanner(
|
|
279
|
-
false,
|
|
280
|
-
true,
|
|
281
|
-
1500,
|
|
282
|
-
result.newWarningThreshold,
|
|
283
|
-
)
|
|
284
|
-
expect(result.newWarningThreshold).toBe(null) // cleared
|
|
285
|
-
|
|
286
|
-
// Finally: dropped again - should warn again
|
|
287
|
-
result = shouldAutoShowBanner(
|
|
288
|
-
false,
|
|
289
|
-
true,
|
|
290
|
-
50,
|
|
291
|
-
result.newWarningThreshold,
|
|
292
|
-
)
|
|
293
|
-
expect(result.shouldShow).toBe(true)
|
|
294
|
-
expect(result.newWarningThreshold).toBe(100)
|
|
295
|
-
})
|
|
296
|
-
})
|
|
297
|
-
})
|
|
298
|
-
})
|