test: expand unit test coverage across utils, APIs, and services#934
test: expand unit test coverage across utils, APIs, and services#934PeterDaveHello wants to merge 1 commit intoChatGPTBox-dev:masterfrom
Conversation
Add 172 tests (110 → 282) covering 14 new/extended test files.
Utilities & config:
- model-name-convert: extend to 100% (modelNameToDesc, getModelValue, isUsingModelName)
- eventsource-parser: extend to 100% (CR/LF, BOM, comments, partial buffers)
- crop-text: new tests for cropping, tiktoken paths, clamp behavior
- jwt-token-generator: new tests for generation, caching, expiry
- config predicates: complete isUsing* predicates + getPreferredLanguageKey
Content script:
- selection-tools: new tests for all 11 tools' genPrompt functions
Services & sessions:
- local-session: new CRUD tests (create, switch, delete, reset)
- init-session: new session shape validation tests
- wrappers/registerPortListener: new tests for port message flow, token getters
API adapters:
- custom-api: new tests for all response schemas, [DONE], finish_reason, errors
- azure-openai-api: new tests for URL composition, deployment fallback, headers
- claude-api: new tests for streaming, message_stop, header construction
- thin adapters (aiml, deepseek, moonshot, openrouter, chatglm): delegation + key forwarding
Background:
- Extract redactSensitiveFields to src/background/redact.mjs for testability
- New tests for sensitive key redaction, prompt/selection integration, depth limits
Bug fixes found during testing:
- custom-api.mjs: add optional chaining on data.choices (lines 79-81, 92) to prevent
TypeError when APIs return {response} format without choices field (e.g. Ollama)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
📝 WalkthroughWalkthroughThis PR refactors redaction logic into a dedicated module, adds optional chaining safety guards to API response parsing, introduces a new ESM loader hook for JSX and CommonJS interoperability in tests, and adds comprehensive unit test coverage across the codebase. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request focuses on substantially improving the project's test suite by expanding coverage for existing utilities and services, and introducing new tests for previously untested modules and API integrations. The changes aim to enhance code robustness, prevent regressions, and ensure consistent behavior across various model providers and internal functionalities. A minor bug related to custom API response parsing was also addressed as a direct result of the expanded testing efforts. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
||||||||||||
There was a problem hiding this comment.
Pull request overview
Expands unit test coverage across core utilities, API adapters, content-script selection tools, and session/wrapper services, and includes a small runtime hardening fix for the custom API SSE parser plus a refactor to make background redaction logic testable.
Changes:
- Add/extend unit tests across utils, config predicates, content-script tools, services/sessions, and multiple API adapters.
- Refactor background sensitive-field redaction into
src/background/redact.mjsand add dedicated tests. - Fix
custom-api.mjsSSE parsing to avoidTypeErrorwhenchoicesis absent by using optional chaining.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/utils/model-name-convert.test.mjs | Expands coverage for model name conversions, grouping, and selection predicates. |
| tests/unit/utils/jwt-token-generator.test.mjs | Adds tests for JWT generation, verification, caching, and invalid key formats. |
| tests/unit/utils/eventsource-parser.test.mjs | Extends parser coverage for CR/LF variants, comments, partial buffers, and retry handling. |
| tests/unit/utils/crop-text.test.mjs | Adds tests for cropping behavior, punctuation splitting, clamp boundaries, and token-vs-char modes. |
| tests/unit/services/wrappers-register.test.mjs | Adds tests for port listener flow and cookie/token getter helpers. |
| tests/unit/services/local-session.test.mjs | Adds CRUD/reset/persistence tests for local session storage helpers. |
| tests/unit/services/init-session.test.mjs | Adds tests for session shape defaults and aiName derivation behavior. |
| tests/unit/services/apis/thin-adapters.test.mjs | Adds delegation/header forwarding tests for thin API adapters. |
| tests/unit/services/apis/custom-api.test.mjs | Adds comprehensive SSE schema/edge-case/error-path tests for the custom API adapter. |
| tests/unit/services/apis/claude-api.test.mjs | Adds streaming, header/body composition, and error handling tests for Claude API adapter. |
| tests/unit/services/apis/azure-openai-api.test.mjs | Adds URL composition, deployment fallback, headers, and streaming tests for Azure OpenAI adapter. |
| tests/unit/content-script/selection-tools.test.mjs | Adds genPrompt output tests for all selection tools, including language preference behavior. |
| tests/unit/config/config-predicates.test.mjs | Extends coverage for provider isUsing* predicates and preferred language resolution. |
| tests/unit/background/redact.test.mjs | Adds tests for sensitive key redaction, prompt/selection integration, depth and circular handling. |
| tests/setup/jsx-loader-hooks.mjs | Introduces a custom ESM loader hook to enable importing JSX-containing .mjs in tests. |
| src/services/apis/custom-api.mjs | Prevents crashes on SSE chunks missing choices by optional chaining access. |
| src/background/redact.mjs | Extracts redaction logic into a standalone module for reuse and testability. |
| src/background/index.mjs | Switches background logging redaction to the extracted module (removes inlined implementation). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import { afterEach, before, describe, test } from 'node:test' | ||
|
|
||
| // Register JSX/CJS loader hooks so the selection-tools module can be imported. | ||
| register('./tests/setup/jsx-loader-hooks.mjs', 'file://' + process.cwd() + '/') |
There was a problem hiding this comment.
module.register() is being called with a parent URL built via 'file://' + process.cwd() + '/'. On Windows this produces an invalid file URL (e.g. file://C:\...) and can cause the loader hook registration to fail. Use a proper file URL (e.g. import.meta.url as the parent URL, or pathToFileURL(process.cwd() + '/').href) so tests are cross-platform.
| register('./tests/setup/jsx-loader-hooks.mjs', 'file://' + process.cwd() + '/') | |
| register('./tests/setup/jsx-loader-hooks.mjs', import.meta.url) |
| export function isPromptOrSelectionLikeKey(lowerKey) { | ||
| const normalizedKey = lowerKey.replace(/[^a-z0-9]/g, '') | ||
| return ( | ||
| normalizedKey.endsWith('question') || | ||
| normalizedKey.endsWith('prompt') || | ||
| normalizedKey.endsWith('query') || | ||
| normalizedKey === 'selection' || | ||
| normalizedKey === 'selectiontext' | ||
| ) |
There was a problem hiding this comment.
isPromptOrSelectionLikeKey assumes its argument is already lowercased, but it’s exported and will return incorrect results for mixed-case keys (uppercase letters get stripped by /[^a-z0-9]/g, so e.g. userQuestion won’t match). To make the helper safe for direct use, normalize inside the function (e.g. lowercase before stripping) or rename it to make the requirement explicit and keep it non-exported.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
tests/unit/utils/eventsource-parser.test.mjs (1)
65-175: Optional: extract repeated parser setup into a tiny helper.This block repeats the same
parsed/parserinitialization in each test. A helper would reduce noise and make future additions easier.♻️ Suggested refactor
const encoder = new TextEncoder() const toBytes = (text) => encoder.encode(text) + +const setupParser = () => { + const parsed = [] + const parser = createParser((event) => parsed.push(event)) + return { parsed, parser } +} test('createParser handles \\r\\n line endings', () => { - const parsed = [] - const parser = createParser((event) => parsed.push(event)) + const { parsed, parser } = setupParser()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/utils/eventsource-parser.test.mjs` around lines 65 - 175, The tests repeat the same parsed/parser setup; extract a small helper (e.g., withParsedParser or createTestParser) that encapsulates "const parsed = []; const parser = createParser((event) => parsed.push(event));" and returns or invokes the test body with parsed and parser so each test uses that helper instead of duplicating those lines; update all tests that call createParser/toBytes to use the new helper (references: createParser, parsed, parser, toBytes) to keep behavior identical while reducing repetition.tests/unit/utils/crop-text.test.mjs (1)
190-194: Assertion may be brittle if lengths coincidentally match.The
assert.notEqualassumes tiktoken and character modes will always produce different cropped lengths for this input. While usually true, edge cases exist where both modes could produce the same length by coincidence, causing a flaky test.Consider asserting that cropping occurred in both modes (which you already do) and either removing this assertion or accepting the small flakiness risk for the added coverage value.
💡 Alternative: soften assertion to informational check
- // Token counting vs char counting should produce different results - assert.notEqual( - resultTiktoken.length, - resultChars.length, - 'tiktoken and char modes should produce different crop lengths', - ) + // Token counting vs char counting typically produce different results + // (not strictly required, but expected for most inputs) + if (resultTiktoken.length === resultChars.length) { + console.log('Note: tiktoken and char modes produced same length (possible but rare)') + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/utils/crop-text.test.mjs` around lines 190 - 194, The brittle assertion comparing resultTiktoken.length and resultChars.length should be removed or softened; instead, drop the assert.notEqual check and rely on existing assertions that cropping occurred for both modes (resultTiktoken.cropped and resultChars.cropped) or convert the length comparison into a non-failing informational/logging check so the test doesn't flake when lengths coincidentally match; update the test that references resultTiktoken and resultChars accordingly.tests/unit/services/wrappers-register.test.mjs (1)
205-221: Consider using a more deterministic wait mechanism.The 50ms
setTimeoutdelays (lines 217, 244) for waiting on async handlers could occasionally cause flakiness in CI environments under load. While this works in practice, consider using a polling approach or exposing the internal promise for more deterministic testing.// Alternative: poll for expected state const waitFor = async (predicate, timeout = 500, interval = 10) => { const start = Date.now() while (Date.now() - start < timeout) { if (predicate()) return await new Promise((r) => setTimeout(r, interval)) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/services/wrappers-register.test.mjs` around lines 205 - 221, The test uses a fixed 50ms setTimeout to wait for async handlers which can be flaky; replace the hard sleep in tests/unit/services/wrappers-register.test.mjs with a deterministic wait (e.g., add a local waitFor(predicate, timeout, interval) helper) and use it to poll until executor.mock.calls.length === expected value (or port.postedMessages length) after calling registerPortListener and port.emitMessage; alternatively adjust the tested code (registerPortListener) to expose or return a promise that the test can await and use that promise instead of setTimeout.src/background/redact.mjs (1)
1-10: Consider adding 'cookie' to SENSITIVE_KEYWORDS.The codebase handles various cookies (Bard, Bing, Claude session keys) that contain authentication data. Adding 'cookie' to
SENSITIVE_KEYWORDSwould provide defense-in-depth for any future cookie-related fields in logged objects.💡 Suggested addition
const SENSITIVE_KEYWORDS = [ 'apikey', // Covers apiKey, customApiKey, claudeApiKey, etc. 'token', // Covers accessToken, refreshToken, etc. 'secret', 'password', 'kimimoonshotrefreshtoken', 'credential', 'jwt', 'session', + 'cookie', ]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/background/redact.mjs` around lines 1 - 10, Add the string 'cookie' to the SENSITIVE_KEYWORDS array so cookie-related fields (e.g., Bard/Bing/Claude session cookies) are treated as sensitive; update the SENSITIVE_KEYWORDS constant in redact.mjs (where it's defined) to include 'cookie' alongside 'apikey', 'token', etc., and run/update any unit tests that validate redaction behavior to cover cookie-named fields to ensure case-insensitive matching continues to work.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/setup/jsx-loader-hooks.mjs`:
- Around line 30-37: The generated shim strings currently include semicolons at
the end of statements (see the template lines producing `import { createRequire
as _cr } from 'node:module';`, `const _req = _cr(...) ;`, `const _mod =
_req(...) ;`, each `export const ${id} = _mod[...] ;` and `export default
_mod;`); update those template literals to remove the trailing semicolons so the
produced module source matches the repo style (no semicolons) while preserving
the exact identifiers (`_cr`, `_req`, `_mod`, the generated `export const ${id}`
lines and `export default _mod`) and string interpolation logic.
- Line 8: JSX_RE is too narrow (only matches capitalized component tags) and
misses lowercase HTML tags and fragments; update the regex used for JSX
detection (JSX_RE) to match any tag name and fragment starts — e.g. replace
/<[A-Z][A-Za-z0-9]*[\s/>]/ with a pattern that also matches lowercase names and
the fragment opener like /<(?:[A-Za-z][A-Za-z0-9-]*|>)[\s/>]/ (and apply the
same replacement to the other occurrences referenced around lines 47-55) so .mjs
JSX files and <>...</> fragments are detected and transformed.
---
Nitpick comments:
In `@src/background/redact.mjs`:
- Around line 1-10: Add the string 'cookie' to the SENSITIVE_KEYWORDS array so
cookie-related fields (e.g., Bard/Bing/Claude session cookies) are treated as
sensitive; update the SENSITIVE_KEYWORDS constant in redact.mjs (where it's
defined) to include 'cookie' alongside 'apikey', 'token', etc., and run/update
any unit tests that validate redaction behavior to cover cookie-named fields to
ensure case-insensitive matching continues to work.
In `@tests/unit/services/wrappers-register.test.mjs`:
- Around line 205-221: The test uses a fixed 50ms setTimeout to wait for async
handlers which can be flaky; replace the hard sleep in
tests/unit/services/wrappers-register.test.mjs with a deterministic wait (e.g.,
add a local waitFor(predicate, timeout, interval) helper) and use it to poll
until executor.mock.calls.length === expected value (or port.postedMessages
length) after calling registerPortListener and port.emitMessage; alternatively
adjust the tested code (registerPortListener) to expose or return a promise that
the test can await and use that promise instead of setTimeout.
In `@tests/unit/utils/crop-text.test.mjs`:
- Around line 190-194: The brittle assertion comparing resultTiktoken.length and
resultChars.length should be removed or softened; instead, drop the
assert.notEqual check and rely on existing assertions that cropping occurred for
both modes (resultTiktoken.cropped and resultChars.cropped) or convert the
length comparison into a non-failing informational/logging check so the test
doesn't flake when lengths coincidentally match; update the test that references
resultTiktoken and resultChars accordingly.
In `@tests/unit/utils/eventsource-parser.test.mjs`:
- Around line 65-175: The tests repeat the same parsed/parser setup; extract a
small helper (e.g., withParsedParser or createTestParser) that encapsulates
"const parsed = []; const parser = createParser((event) => parsed.push(event));"
and returns or invokes the test body with parsed and parser so each test uses
that helper instead of duplicating those lines; update all tests that call
createParser/toBytes to use the new helper (references: createParser, parsed,
parser, toBytes) to keep behavior identical while reducing repetition.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (18)
src/background/index.mjssrc/background/redact.mjssrc/services/apis/custom-api.mjstests/setup/jsx-loader-hooks.mjstests/unit/background/redact.test.mjstests/unit/config/config-predicates.test.mjstests/unit/content-script/selection-tools.test.mjstests/unit/services/apis/azure-openai-api.test.mjstests/unit/services/apis/claude-api.test.mjstests/unit/services/apis/custom-api.test.mjstests/unit/services/apis/thin-adapters.test.mjstests/unit/services/init-session.test.mjstests/unit/services/local-session.test.mjstests/unit/services/wrappers-register.test.mjstests/unit/utils/crop-text.test.mjstests/unit/utils/eventsource-parser.test.mjstests/unit/utils/jwt-token-generator.test.mjstests/unit/utils/model-name-convert.test.mjs
| import { fileURLToPath } from 'node:url' | ||
| import { createRequire } from 'node:module' | ||
|
|
||
| const JSX_RE = /<[A-Z][A-Za-z0-9]*[\s/>]/ |
There was a problem hiding this comment.
Expand JSX detection beyond uppercase component tags.
JSX_RE currently misses valid JSX like <div /> and fragments (<>...</>), so those .mjs files won’t be transformed and can fail at load time.
🔧 Suggested fix
-const JSX_RE = /<[A-Z][A-Za-z0-9]*[\s/>]/
+const JSX_RE = /<(?:[A-Za-z][A-Za-z0-9-]*|>)[\s/>]?/Also applies to: 47-55
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/setup/jsx-loader-hooks.mjs` at line 8, JSX_RE is too narrow (only
matches capitalized component tags) and misses lowercase HTML tags and
fragments; update the regex used for JSX detection (JSX_RE) to match any tag
name and fragment starts — e.g. replace /<[A-Z][A-Za-z0-9]*[\s/>]/ with a
pattern that also matches lowercase names and the fragment opener like
/<(?:[A-Za-z][A-Za-z0-9-]*|>)[\s/>]/ (and apply the same replacement to the
other occurrences referenced around lines 47-55) so .mjs JSX files and <>...</>
fragments are detected and transformed.
| `import { createRequire as _cr } from 'node:module';`, | ||
| `const _req = _cr(${JSON.stringify(url)});`, | ||
| `const _mod = _req(${JSON.stringify(pkg)});`, | ||
| ...names.map((n) => { | ||
| const id = IDENT_RE.test(n) ? n : toSafe(n) | ||
| return `export const ${id} = _mod[${JSON.stringify(n)}];` | ||
| }), | ||
| `export default _mod;`, |
There was a problem hiding this comment.
Remove semicolons in generated shim source to match repo style.
The generated module source currently emits semicolon-terminated statements.
🎯 Suggested fix
- `import { createRequire as _cr } from 'node:module';`,
- `const _req = _cr(${JSON.stringify(url)});`,
- `const _mod = _req(${JSON.stringify(pkg)});`,
+ `import { createRequire as _cr } from 'node:module'`,
+ `const _req = _cr(${JSON.stringify(url)})`,
+ `const _mod = _req(${JSON.stringify(pkg)})`,
...names.map((n) => {
const id = IDENT_RE.test(n) ? n : toSafe(n)
- return `export const ${id} = _mod[${JSON.stringify(n)}];`
+ return `export const ${id} = _mod[${JSON.stringify(n)}]`
}),
- `export default _mod;`,
+ `export default _mod`,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| `import { createRequire as _cr } from 'node:module';`, | |
| `const _req = _cr(${JSON.stringify(url)});`, | |
| `const _mod = _req(${JSON.stringify(pkg)});`, | |
| ...names.map((n) => { | |
| const id = IDENT_RE.test(n) ? n : toSafe(n) | |
| return `export const ${id} = _mod[${JSON.stringify(n)}];` | |
| }), | |
| `export default _mod;`, | |
| `import { createRequire as _cr } from 'node:module'`, | |
| `const _req = _cr(${JSON.stringify(url)})`, | |
| `const _mod = _req(${JSON.stringify(pkg)})`, | |
| ...names.map((n) => { | |
| const id = IDENT_RE.test(n) ? n : toSafe(n) | |
| return `export const ${id} = _mod[${JSON.stringify(n)}]` | |
| }), | |
| `export default _mod`, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/setup/jsx-loader-hooks.mjs` around lines 30 - 37, The generated shim
strings currently include semicolons at the end of statements (see the template
lines producing `import { createRequire as _cr } from 'node:module';`, `const
_req = _cr(...) ;`, `const _mod = _req(...) ;`, each `export const ${id} =
_mod[...] ;` and `export default _mod;`); update those template literals to
remove the trailing semicolons so the produced module source matches the repo
style (no semicolons) while preserving the exact identifiers (`_cr`, `_req`,
`_mod`, the generated `export const ${id}` lines and `export default _mod`) and
string interpolation logic.
There was a problem hiding this comment.
Code Review
This pull request significantly expands unit test coverage across various parts of the application, including utilities, APIs, and services, which is a great improvement for code quality and reliability. The changes also include a bug fix in the custom API adapter and a refactoring of the sensitive data redaction logic for better testability. My review focuses on a few opportunities to improve code clarity and maintainability in the new redact.mjs module.
| export function isPromptOrSelectionLikeKey(lowerKey) { | ||
| const normalizedKey = lowerKey.replace(/[^a-z0-9]/g, '') | ||
| return ( | ||
| normalizedKey.endsWith('question') || | ||
| normalizedKey.endsWith('prompt') || | ||
| normalizedKey.endsWith('query') || | ||
| normalizedKey === 'selection' || | ||
| normalizedKey === 'selectiontext' | ||
| ) | ||
| } |
There was a problem hiding this comment.
For better readability and maintainability, you can refactor this function to use arrays for storing matching patterns and then use array methods like some() and includes() for checking. This approach avoids a long chain of || operators, making it easier to add or remove conditions in the future.
export function isPromptOrSelectionLikeKey(lowerKey) {
const normalizedKey = lowerKey.replace(/[^a-z0-9]/g, '');
const suffixes = ['question', 'prompt', 'query'];
const exactMatches = ['selection', 'selectiontext'];
return (
suffixes.some((suffix) => normalizedKey.endsWith(suffix)) ||
exactMatches.includes(normalizedKey)
);
}| let isKeySensitive = isPromptOrSelectionLikeKey(lowerKey) | ||
| if (!isKeySensitive) { | ||
| for (const keyword of SENSITIVE_KEYWORDS) { | ||
| if (lowerKey.includes(keyword)) { | ||
| isKeySensitive = true | ||
| break | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
The logic for determining if a key is sensitive can be simplified. Instead of using a for...of loop inside an if block, you can combine the checks into a single, more concise expression using the some() array method. This improves readability and reduces nesting.
const isKeySensitive =
isPromptOrSelectionLikeKey(lowerKey) ||
SENSITIVE_KEYWORDS.some((keyword) => lowerKey.includes(keyword));
User description
Add 172 tests (110 → 282) covering 14 new/extended test files.
Utilities & config:
Content script:
Services & sessions:
API adapters:
Background:
Bug fixes found during testing:
PR Type
Tests, Bug fix
Description
Add 172 tests (110 → 282) across 14 new/extended test files
Fix optional chaining on
data.choicesin custom-api.mjs{response}format without choices field (e.g. Ollama)Extract
redactSensitiveFieldstosrc/background/redact.mjsfor testabilityDiagram Walkthrough
File Walkthrough
15 files
Comprehensive custom API response schema testsPort listener and token getter integration testsAzure OpenAI URL composition and header testsClaude API streaming and message_stop testsText cropping with tiktoken and clamp behaviorSelection tools genPrompt for all 11 toolsExtended model name conversion to 100% coverageComplete isUsing* predicates for all providersSession CRUD operations and persistence testsThin adapter delegation and key forwarding testsSensitive field redaction and circular reference handlingExtended SSE parser coverage for edge casesSession initialization and shape validation testsJWT generation, caching, and expiry testsESM loader hooks for JSX and CJS interop2 files
Extract redactSensitiveFields for testabilityImport redactSensitiveFields from redact module1 files
Add optional chaining on data.choices accessSummary by CodeRabbit
Release Notes
Bug Fixes
Quality & Stability