feat:Brevo function - #100
Conversation
Add `brevo function list` and `brevo function get <id>` commands for managing Brevo Functions, plus the "Brevo Function" app type choice in `brevo app create`. - New service (src/services/function.ts) with list, draft list, and get - New command handlers (src/commands/function/list.ts, get.ts) - Register functionCommandGroup in definitions.ts and bin/index.ts - Add Function commands section to root help screen - Add brevo_function template flag for app-config.json rendering - Add Brevo Function app type to interactive create prompt (private only) - Unit tests for service, list command, and get command (23 tests) - Command registration tests in definitions.test.ts (4 tests) - Help formatting tests updated for functionCommandGroup - New smoke suite (scripts/smoke/function.ts) exercising list, list --draft, get, and get-not-found against a real account — opt-in via `yarn smoke --suite=function` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add `brevo-function-type` to FEATURE_STAGE as a preview feature - Move function group definition to preview-definitions.ts so esbuild can eliminate it from published builds - Gate the Brevo Function choice in `app create` behind __BREVO_PREVIEW__ - Gate the Function commands section in help.ts behind __BREVO_PREVIEW__ - Add `aliases` support to SubcommandGroupDefinition so `brevo fn list` and `brevo fn get` work as shortcuts - Update tests for the conditional export and gated app-type prompt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add three management commands to the Brevo Function group: - `brevo function activate <id>` — PATCH with is_active: true - `brevo function deactivate <id>` — PATCH with is_active: false - `brevo function delete <id>` — DELETE with --force to skip confirmation All three are gated behind __BREVO_PREVIEW__ alongside the existing list and get commands. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add brevo function activate, deactivate and delete commands
Review dp function
…d SSE error handling - Add `brevo fn init` command with two creation flows: AI generation and global templates - Implement SSE streaming for AI code generation and iteration with colored progress stages - Add preview execution (fetch contacts + execute + results table) after generation and iteration - Auto-derive `attribute_id` from function name in SCREAMING_SNAKE_CASE - Add graceful error handling for SSE stream termination and API failures - Add shared function app selector (`select-function.ts`) used by fn subcommands - Update all fn subcommands to use shared app selector - Set `source: 'cli'` on all dp-functions API calls - Wrap SSE reader.read() errors into ApiError for clean failure messages - Add comprehensive tests for init command and SSE stream module Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The backend updates the draft in the database during iterate (PATCH /generate/stream) in all auth modes, so referencing the draft by ID is consistent with the initial preview and avoids sending the full code payload to the execute endpoint. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The dp-functions backend returns 403 with code `feature_not_enabled` when the account lacks the dp-functions entitlement. Map this to a user-friendly message in both the REST client (ApiClient) and the SSE stream handler so all fn subcommands display: "Brevo Functions is not enabled for this account." Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ernaries - sse-stream.ts: extract performSSEFetch, handleSSEErrorResponse, readChunk, processSSELine, flushSSEState helpers to reduce cognitive complexity from 49 to ~11; replace nested ternary with extractErrorMessage function - init.ts: extract executePreview, saveGeneratedFunction, mergeGenerateResult, updateSpinnerFromEvent, accumulateResult helpers to reduce cognitive complexity of processGenerateStream (38→~8) and aiGenerationFlow (40→~11); combine consecutive push calls into single push with multiple args - list.ts: extract inner template literals to variables to eliminate nesting - templates/index.ts: replace nested ternary with if/else in resolveTemplateFlags - create.ts: replace nested ternary with if/else in buildCreatePayload; extract cacheCreatedAppCredentials to reduce createCommand complexity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Parameterize similar tests in sse-stream.test.ts and definitions.test.ts (S5976) - Add formatCellValue helper for safe object stringification in init.ts (S6551) - Replace boolean statusBadge function with ACTIVE_BADGE/INACTIVE_BADGE constants in list.ts (S2301) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use explicit type checks (string, number, boolean) instead of String(value) on unknown, so SonarCloud can verify no object reaches the default toString path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Attribute ID was shown both as a header line and could appear in the results table. Remove the explicit header line so the preview only shows the description and the data table. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use shared functionService from container instead of local instance (init.ts) - Wire STAGE_LABELS to lang file keys instead of hardcoded strings - Rethrow ApiError/CliError in generate and iterate catch blocks - Guard iterate request and preview against missing draftId - Match duplicate-name detection on HTTP 409 status code, not message copy - Union columns across all rows in printResultsTable - Document AbortSignal.timeout as a deadline in sse-stream.ts - Evaluate statusBadge at call time to respect TTY/NO_COLOR (list.ts) - Change --id [id] to --id <id> in all fn subcommands - Remove orphaned lang keys (stage labels, success messages, file-written) - Hoist distribution_type out of template branches (app-config.json.tmpl) - Remove unused src/__tests__/tsconfig.json Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- init.ts: extract `runIterateRound` and `tryPreview` from `aiGenerationFlow` to reduce cognitive complexity from 21 to within the 15 threshold (typescript:S3776). - list.ts: replace `statusBadge(isActive)` boolean-param function with two named functions `activeBadge()` / `inactiveBadge()` (typescript:S2301). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…provements feat: add brevo fn init with AI generation, templates, and error handling
Add a standalone deploy command that lets users deploy draft Brevo Functions independently of the `brevo fn init` AI generation flow. - New `brevo function deploy` command with `--id` and `--json` flags - Interactive draft picker when `--id` is omitted - Preview with sample contacts before deployment - Name prompt with duplicate-name (409) retry loop - Stop flow with "Unable to deploy function" when preview returns errors - Extract shared preview-table utilities from init.ts into preview-table.ts - 11 test cases covering success, JSON, picker, errors, and edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d boolean param - Extract `deployJsonMode`, `deployInteractive`, and `tryPreview` from the main handler to reduce cognitive complexity (typescript:S3776). - Replace `assertDraftSelectionAllowed(jsonMode?)` boolean-param function with `assertInteractiveTerminal()` that only checks TTY (typescript:S2301). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cription
Sonar flagged `/\s+\S*$/` as super-linear due to backtracking.
Use `lastIndexOf(' ', 50)` instead — same word-boundary cut, O(n).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add `brevo fn deploy` command
Merge origin/main to bring in 110 files of changes. Conflicts resolved in 5 files by keeping the feature branch's function-type additions while incorporating main's non-conflicting updates (UI apps GA). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…unction chore: sync main into features_set-dp-function
Add app-linking step to `function deploy` and `function init` flows. After a function is created, it is linked to the selected app via POST /v3/app-store/app-functions. Adds --app-id flag to deploy for non-interactive use, and an interactive app picker when omitted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract selectFunctionApp/tryLinkFunctionToApp into select-app.ts,
used by both deploy.ts and init.ts (review point 3)
- Fix --json output corruption: link runs silent, spinner uses
{ silent: true }, no logInfo to stdout (point 1)
- Add linked/app_id to JSON payload so callers can detect link
status (point 2)
- Move app picker after preview+confirm to avoid wasted round-trips
on early failure (point 4)
- Remove appId! non-null assertion — narrowed via control flow (point 5)
- Log link error detail behind isDebug via logDebug (point 6)
- Add 5 new tests: --app-id path, --json without --app-id, link
failure in JSON mode, no-apps throw, link failure warning (point 7)
- Fix template literal + concatenation mix in app name display (point 8)
- Add changeset for new --app-id flag (point 8)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move isDuplicateNameError, executePreview, tryPreview, and the name-confirm-deploy loop into deploy-helpers.ts. Both deploy.ts and init.ts now delegate to these shared functions, parameterized by message constants. This addresses the SonarCloud duplication quality gate (11.2% → well below 3%). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Generalize executePreview to accept templateArgs (draft_id or template_id) so init.ts templateFlow uses the shared function instead of an inline duplicate. - Fix nested template literal in select-app.ts (S4624). - Extract test setup into setupHappyPath() helper to reduce duplicated mock boilerplate across 16 tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: link deployed function to app
Move the `brevo function` surface out of the `__BREVO_PREVIEW__` build gate so it ships in every published build. This follows the same pattern as the UI-apps GA transition (BEX-290). Changes: - Flip FEATURE_STAGE['brevo-function-type'] from 'preview' to 'ga' - Move function group definition from preview-definitions.ts to definitions.ts (always-defined, no longer conditional) - Add function handler imports directly in definitions.ts - Remove __BREVO_PREVIEW__ guard from bin/index.ts, help.ts, create.ts - Move all FUNCTION_* strings from preview-messages.ts to en.ts - Add GA markers (listFunctionCommand, initFunctionCommand, deployFunctionCommand) to scripts/build.mjs - Update tests to reflect the GA state Verified: yarn test (1513 pass), yarn build (public), PREVIEW=1 yarn build (preview), yarn lint, yarn format:check — all clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract shared function state-change pattern (ID resolution, spinner, 404 handling, JSON/card output) into buildFunctionActionCommand helper. Activate and deactivate become thin config wrappers; delete delegates execution while keeping its confirmation prompt. Shared logic is tested once in function-action.test.ts, individual tests verify wiring only. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: resolve SonarCloud duplication in function action commands
… tests Extract resolveFunctionId and withNotFoundHandling helpers for get.ts, deduplicate smoke test cleanup with deleteAndAssert and toggle helpers, and remove unused import flagged by code-quality bot. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: reduce SonarCloud duplication in function commands and smoke tests
piyushsarin-sib
left a comment
There was a problem hiding this comment.
Review
Overall the brevo function group is well built: consistent --id / picker / non-TTY / --json handling across every subcommand, a clean standalone SSE reader, and good unit coverage on the function commands. Requesting changes because the third app type is only half-wired into the CLI, and two repo rules (changeset, agent docs) are not met. Details inline; the two findings with no single anchor line are below.
No changeset, no agent-doc update (blocking)
.changeset/ has no pending file for this branch (one was explicitly removed in chore: remove changeset file), and neither agent-context/SKILL.md nor agent-context/AGENTS.md mention brevo function, the fn alias, the new Brevo Function choice in app create, or the "init requires an interactive terminal" rule. CLAUDE.md makes both hard requirements for new user-facing commands, and an installed skill will otherwise keep telling agents these commands do not exist. Please add a minor changeset and document the surface in both files.
Recommendations
- Test the
app createFunction path end to end. The onlycreatetest change asserts the prompt shows three choices. Nothing asserts thebrevo_function: {}payload, the renderedapp-config.json, or that the OAuth feature scaffold is not offered to a Function app. Anapp uploadtest on a scaffolded Function config would have caught the registry gap flagged inline. - CLAUDE.md's app-type text is now stale. It still describes "the two app types" and the registry comment says "three, soon". Once the registry gains the Function type, add a short Brevo Functions section and update the discriminator wording.
- Fill in the PR body. It is empty. With ~40 commits from eight sub-PRs squashed in, reviewers need the surface summary, the new
BREVO_TEST_API_KEY_FUNCTIONsecret requirement, and the dedicated smoke account dependency in writing. - SonarCloud gate is red on new duplicated-lines density (7.7% vs 3%). The inline notes on
function-action.tsand the duplicated message tables should bring it under.
- Add `function` to app-type registry with positive `brevo_function` detection on both config and record paths - Add `function` row to capability matrix (no OAuth flow, no redirect URIs, no account-install) - Add `brevo_function` field to `OAuthApp` type and `AppRecordLike` - Add `brevo function deploy` to help section - Replace IIFE with `resolveFunctionId` in `executeFunctionAction` - Add pagination to `fetchFunctionList` for >50 functions - Wire `ensureFresh` into `SSEStreamDeps` for token refresh before SSE connections - Expose `runEnsureFresh()` on `ApiClient`, plumb through container - Fix comment in templates/index.ts (`brevo_function` is mutually exclusive with oauth, not orthogonal) - Add explanatory comments in init.ts, tsconfig.eslint.json, help.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Config backward compat: legacy configs without app_type parse correctly (field is undefined, not an error) - Config round-trip: app_type survives writeProjectConfig, and legacy configs without it stay absent after write-back - Wire isolation: app_type never leaks into the upload payload (UploadAppPayload has no app_type field) - Template rendering: brevo_function branch renders app_type correctly, all three app types produce the right value - Capability matrix: function type has no OAuth/UI capabilities, only review-lifecycle on public distribution - Record resolution: brevo_function record resolves to function type; blockless records still resolve to UI (not function) - Recoverability: function apps are always recoverable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Aligns with the existing camelCase convention used by other config fields (appId, appName, logoUri). The field is local metadata only and never sent to the server, so this is a config-shape change with no wire impact. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix doc comment in config.ts that said "there is no separate appType key" - Fix comment in en.ts that said "there is no app-type field" - Fix AGENTS.md.tmpl that said "There is no appType key" - Add legacy app_type → appType migration in readProjectConfigAt - Add 3 migration tests (read migration, write-back cleanup, precedence) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename the informational app-type field from camelCase `appType` back to snake_case `app_type` to match the wire-mirrored key convention used by other platform-facing fields (`distribution_type`, `ui_app`, `brevo_function`). The migration logic now migrates the legacy `appType` key to `app_type` on read and drops it on write-back. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add app_type to app-config.json and address PR #100 review
piyushsarin-sib
left a comment
There was a problem hiding this comment.
Went back through the 13 review threads. All are marked resolved and PR #103 is merged into this branch, so the work did land — but three of the replies claim more than what shipped, and I've re-opened those inline. One repo-rule blocker and one CI note below.
Blocker: agent docs not updated
agent-context/SKILL.md and agent-context/AGENTS.md are untouched by this PR — neither mentions brevo function or brevo fn anywhere. CLAUDE.md is explicit that new commands are user-visible behaviour and both files must be updated in the same PR, and that a feature going GA has to land its reference docs in the GA PR. This adds seven commands, a group alias, and a third app type straight to GA, so it needs both files before merge.
SonarCloud: the gate is red, but the analysis is stale
The gate reports new_duplicated_lines_density at 7.7% against a 3% threshold — the same figure as before the dedup work. SonarCloud's last analysis on this PR is dated 2026-09-03 at commit b2134ac, which predates #101, #102 and #103. So the dedup hasn't actually been measured yet and the checked "SonarCloud quality gate" box in the description isn't supported either way. Worth re-running before merge so we know whether it's fixed.
Re-opened inline (was marked resolved)
- High —
brevo app uploadis still broken for a Function app. The registry half landed;upload.tsis unchanged and still branches onisUiAppConfig. - Medium — only one of the two list reads was paginated; drafts still cap at 50, which makes
fn deploy --idreport a false not-found. - Medium — the SSE 401 mapping.
ensureFreshlanded, the 401 path didn't.
New
- The new pagination loop can spin forever on a short page;
has_moreis still unread. detectRecordassumes the server echoesbrevo_functionon the app record — worth confirming, since that's what decides whetherapp listandscaffoldbootstrap treat a Function app correctly.- SSE reader is released without being cancelled.
oauthAppType.detectRecordgained a condition no caller exercises.--jsononfunction initcan only error.
Nits
- The description says
--drafts; the flag is--draft. - Three user-facing strings live outside
src/lang/en.ts:Description:(init.ts:324),Name:(deploy-helpers.ts:145),'Untitled Function'(deploy.ts:75). Spinner labels are inline elsewhere in the repo so those are fine as-is; these three aren't. help.ts'sgatedSection('brevo-function-type', ...)— the comment now honestly says the definitions carry norequires, so the emergency flip back topreviewit describes wouldn't actually gate the commands. Fixing the comment rather than the gate is defensible, but it's worth making that call explicitly rather than by omission.fn init's iterate loop offers only update / save, with no way out short of Ctrl-C.
…e with legacy migration (#104) * refactor(config): normalize app-config.json keys to snake_case with legacy migration Rename the camelCase keys in app-config.json to snake_case, matching the wire contract: appId → app_id, appName → app_name, logoUri → logo_uri, appType → app_type, auth.redirectUris → auth.redirect_uris. The ProjectConfig interface now IS the file shape, so every read site was found by the compiler. Backward compatibility: readProjectConfigAt folds the legacy camelCase keys (and the older auth.redirectUrls) through one alias table, drops them from the returned object, and warns once on stderr when a file carries both spellings with different values. Migration on write: create/upload/scaffold/start now emit snake_case only. upload and scaffold also rewrite an in-sync legacy file on their no-op paths via migrateProjectConfigKeys(), so one run migrates a project regardless of drift. Values are never changed. Also: template, scaffold docs, agent-context docs, README, smoke scripts (accept both spellings so published-build runs still pass), tests for camelCase-only / snake_case-only / mixed reads and the write-back migration, and a changeset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(config): announce key migration on every rewrite and add agent migration note Print the snake_case migration notice whenever a legacy app-config.json is rewritten — after an upload push and after a consented scaffold refresh, not only on the no-op paths — and word it so users know to update their own scripts that read the file. Add an explicit migration rule to agent-context/SKILL.md and AGENTS.md so agents steer custom code to the new key names and never write the camelCase names. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(upload): assert the block error pre-empts the app_type label check Pin the ordering `assertAppTypeAgrees` documents: a config that is BOTH mislabelled and structurally broken must report the structural problem, so a partner is never sent off to fix a label only to hit the real refusal on the next upload. Asserted positively (the entry-named `.label:` error) plus an explicit check that the message does not mention `app_type`, rather than via `rejects.not.toThrow`, which can pass for the wrong reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: describe app_type as a validated label, not a discriminator `app upload` now reads `app_type` to check it agrees with the blocks, so three docs that said the CLI never reads it were wrong as written. - CLAUDE.md: read for validation, never for detection; the pre-flight ordering and why it is last; and the wire decision, which was nowhere stated — the field is file-only because `UploadAppPayload` and the create body are closed structs built key by key, so nothing strips it and bo-be needs no change. - The scaffolded AGENTS.md template said "the CLI never reads it". It now says what the field is for and that a hand-edited block needs the label updated. - agent-context/SKILL.md and AGENTS.md documented the key but not its meaning; both now carry the same paragraph, kept in sync per the repo rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(changeset): fold the snake_case entry into the branch's single changeset CLAUDE.md asks for one changeset file per branch, appended to rather than multiplied. Merges the separate snake_case file into the existing one and adds the app_type agreement check. Bump level stays `minor` — both entries already warranted it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
- Add Brevo Functions documentation to SKILL.md and AGENTS.md (was
missing entirely — agents had no knowledge of the function commands)
- Fix `brevo app upload` for Function apps: skip redirect_uris check,
send `brevo_function: {}` on the wire, omit `auth` block, handle
write-back and diff rendering correctly for the third app type
- Paginate `fetchDraftFunctionList` (was capped at 50, causing silent
truncation and false not-found errors in `brevo fn deploy --id`)
- Fix `fetchFunctionList` pagination loop: use `has_more` + empty-page
guard instead of `total` comparison to prevent infinite loops
- Refactor `executeFunctionAction` to delegate to `withNotFoundHandling`
instead of re-implementing the 404 catch block (Sonar duplication)
- Move hardcoded strings (`Description:`, `Name:`, `Untitled Function`)
to `src/lang/en.ts`
- Remove unused `functionService` mock from function-action.test.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge the snake_case config key rename (redirect_uris, app_id) from the base branch with the function-app upload support from this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: address remaining PR #100 review comments for brevo function
…key (transition step) (#106) feat(json): emit snake_case twins for every camelCase --json key (transition step) Machine-readable output had two conventions: pass-through commands (app list, app submit, function *) emit the wire's snake_case, while CLI-built objects (create, upload, credentials, install, delete, whoami, the error envelope) emit camelCase. app-config.json settled on snake_case in BEX-470, and scripts should be able to read one spelling everywhere. Renaming outright would break every `jq .appId` in a pipeline with nothing the CLI could migrate, so this is the deprecation step: jsonOutput() now adds a snake_case twin after each camelCase key at the top level of every document (per element for arrays) and inside the error envelope. redirectUri / redirectUris alias to the wire name redirect_uris. Nested objects are left untouched — they are wire records or user data. Nothing is removed; camelCase goes away in the next major. Agent docs and the changeset carry the deprecation note; the seven tests that pinned exact camelCase shapes now pin both spellings. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
|


Summary
Add the full
brevo function(aliasbrevo fn) command surface for creating, managing, and deploying Brevo Functions from the CLI.New commands
brevo function list— List all Brevo Functions on the account (supports--json,--drafts)brevo function get— Show details of a single function by--idor interactive selectionbrevo function activate— Activate a functionbrevo function deactivate— Deactivate a functionbrevo function delete— Delete a function (with confirmation prompt,--forceto skip)brevo fn init— Interactive AI-powered function creation: describe intent, pick a template or generate from scratch, preview with sample contacts, iterate, and deploybrevo fn deploy— Deploy a draft function to production and optionally link it to an appKey implementation details
--jsonfor scripted/CI usage and--idfor non-interactive targetingfunction-action.ts(resolveFunctionId,withNotFoundHandling,executeFunctionAction,buildFunctionActionCommand) eliminate duplication across action commandsselect-function.tsprovides reusable interactive function picker withassertFunctionSelectionAllowedguard for non-TTY contextsdeploy-helpers.tsextracts shared deploy logic (app selection, linking) to reduce cognitive complexitypreview-table.tsrenders function preview results as formatted tablesapi/sse-stream.tsfor real-time AI generation feedback duringfn initSmoke tests
functionsuite added to the smoke test runner covering: list, get, activate/deactivate cycle, deploy, init (with PTY interaction), and cleanupsmoke.ymlfor CI (pre-merge, post-merge, and manual dispatch)Other changes
src/lang/en.tsfor all function commandssrc/lib/constants.tsFEATURE_STAGE['function']set to'ga'insrc/lib/preview.tsfunctionServiceadded tosrc/container.tsandsrc/services/function.tsTest plan