@alexkroman1/aai-cli 5.13.2 → 6.1.0
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/dist/{_agent-C8Jk-ZcP.mjs → _agent-vGW5SBnX.mjs} +23 -5
- package/dist/_agent.d.ts +18 -0
- package/dist/{_api-client-BX8Qv51n.mjs → _api-client-LgLksMhN.mjs} +45 -8
- package/dist/_api-client.d.ts +39 -0
- package/dist/{_bundler-BuIyDdvs.mjs → _bundler-BapCUUJN.mjs} +42 -14
- package/dist/_bundler.d.ts +43 -5
- package/dist/_cli-common.d.ts +67 -3
- package/dist/{_config-CzwAWOWr.mjs → _config-D_s09e7g.mjs} +36 -18
- package/dist/_deploy.d.ts +2 -5
- package/dist/_dev-restart.d.ts +6 -1
- package/dist/{_dev-server-Ch7K8e8i.mjs → _dev-server-DQ1t9sbw.mjs} +94 -38
- package/dist/_dev-server.d.ts +30 -2
- package/dist/_e2e-test-utils.d.ts +80 -0
- package/dist/_fault-mode.d.ts +175 -0
- package/dist/{_init-CbkrE2wq.mjs → _init-CpISqoR8.mjs} +3 -3
- package/dist/{_output-CC300DzW.mjs → _output-CKkmWs7i.mjs} +8 -1
- package/dist/_output.d.ts +9 -2
- package/dist/{_server-common-61fL__39.mjs → _server-common-6e0QI4mq.mjs} +1 -1
- package/dist/{_slug-api-D30zPmsn.mjs → _slug-api-D2heILuC.mjs} +2 -2
- package/dist/{_studio-D2ciIhbi.mjs → _studio-zAJycZ8j.mjs} +3 -3
- package/dist/{_templates-B1kt3BGt.mjs → _templates-iDGjGiPf.mjs} +4 -6
- package/dist/{_typecheck-gate-4v9UBZHh.mjs → _typecheck-gate-BXvUNnfs.mjs} +1 -1
- package/dist/{_utils-0DHbcqUb.mjs → _utils-B8QmtFhK.mjs} +21 -7
- package/dist/_utils.d.ts +18 -2
- package/dist/{_vite-env-Dg_QlVv0.mjs → _vite-env-BNveawd1.mjs} +31 -1
- package/dist/_vite-env.d.ts +30 -0
- package/dist/build-BO1Ni6oJ.mjs +62 -0
- package/dist/build.d.ts +19 -0
- package/dist/cli-workflow.d.ts +10 -0
- package/dist/cli.d.ts +1 -2
- package/dist/cli.mjs +406 -224
- package/dist/{client-bundler-C9qXL8A2.mjs → client-bundler-B8Q9F8gJ.mjs} +3 -22
- package/dist/client-bundler.mjs +1 -1
- package/dist/{delete-DCRX3d96.mjs → delete-4I0uNR4a.mjs} +6 -7
- package/dist/delete.d.ts +2 -8
- package/dist/{deploy-DjsEV-40.mjs → deploy-DJ3HRcia.mjs} +13 -14
- package/dist/{dev-CvPH5dbF.mjs → dev-C6SWaAZP.mjs} +3 -3
- package/dist/{eject-LgBRfnbH.mjs → eject-Zrzk5KV3.mjs} +41 -15
- package/dist/eject.d.ts +30 -3
- package/dist/{init-BppxMR8r.mjs → init-BTRGiPQr.mjs} +61 -26
- package/dist/init.d.ts +11 -0
- package/dist/{login-BQQaxJhL.mjs → login-k0Z3VQu9.mjs} +5 -5
- package/dist/project-config.mjs +1 -1
- package/dist/scaffold/CLAUDE.md +498 -91
- package/dist/scaffold/package.json +11 -8
- package/dist/scaffold/pnpm-workspace.yaml +32 -0
- package/dist/scaffold/server.mjs +53 -57
- package/dist/{secret-Ds9dvqAj.mjs → secret-DzSQx1Ds.mjs} +6 -3
- package/dist/{storage-DbrQSHFk.mjs → storage-C-Do0mIA.mjs} +18 -6
- package/dist/{studio-GSFd4s5O.mjs → studio-BGVmCA2G.mjs} +10 -10
- package/dist/templates/code-interpreter/agent.ts +0 -2
- package/dist/templates/dispatch-center/agent.test.ts +21 -30
- package/dist/templates/dispatch-center/agent.ts +4 -31
- package/dist/templates/dispatch-center/client.tsx +41 -28
- package/dist/templates/dispatch-center/shared.ts +73 -66
- package/dist/templates/dispatch-center/tools/incident_add_note.ts +13 -15
- package/dist/templates/dispatch-center/tools/incident_create.ts +42 -45
- package/dist/templates/dispatch-center/tools/incident_escalate.ts +60 -62
- package/dist/templates/dispatch-center/tools/incident_get.ts +10 -6
- package/dist/templates/dispatch-center/tools/incident_triage.ts +44 -46
- package/dist/templates/dispatch-center/tools/incident_update_status.ts +40 -42
- package/dist/templates/dispatch-center/tools/ops_dashboard.ts +3 -6
- package/dist/templates/dispatch-center/tools/ops_protocols.ts +1 -1
- package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +21 -24
- package/dist/templates/dispatch-center/tools/resources_dispatch.ts +53 -55
- package/dist/templates/dispatch-center/tools/resources_get_available.ts +3 -5
- package/dist/templates/dispatch-center/tools/resources_update_status.ts +40 -43
- package/dist/templates/embedded-assets/agent.test.ts +83 -0
- package/dist/templates/embedded-assets/agent.ts +1 -48
- package/dist/templates/embedded-assets/shared.ts +17 -0
- package/dist/templates/embedded-assets/system-prompt.md +13 -0
- package/dist/templates/embedded-assets/tools/list_topics.ts +9 -0
- package/dist/templates/embedded-assets/tools/search_knowledge.ts +26 -0
- package/dist/templates/health-assistant/agent.test.ts +169 -0
- package/dist/templates/health-assistant/agent.ts +1 -166
- package/dist/templates/health-assistant/fda.ts +100 -0
- package/dist/templates/health-assistant/tools/check_drug_interaction.ts +59 -0
- package/dist/templates/health-assistant/tools/medication_lookup.ts +31 -0
- package/dist/templates/infocom-adventure/agent.test.ts +205 -0
- package/dist/templates/infocom-adventure/agent.ts +3 -106
- package/dist/templates/infocom-adventure/client.tsx +32 -28
- package/dist/templates/infocom-adventure/shared.ts +36 -16
- package/dist/templates/infocom-adventure/tools/game_state_drop.ts +13 -0
- package/dist/templates/infocom-adventure/tools/game_state_flag.ts +13 -0
- package/dist/templates/infocom-adventure/tools/game_state_get.ts +16 -0
- package/dist/templates/infocom-adventure/tools/game_state_history.ts +14 -0
- package/dist/templates/infocom-adventure/tools/game_state_move.ts +14 -0
- package/dist/templates/infocom-adventure/tools/game_state_restart.ts +13 -0
- package/dist/templates/infocom-adventure/tools/game_state_score.ts +13 -0
- package/dist/templates/infocom-adventure/tools/game_state_take.ts +13 -0
- package/dist/templates/link-digest/agent.test.ts +223 -0
- package/dist/templates/link-digest/agent.ts +79 -0
- package/dist/templates/link-digest/client.tsx +146 -0
- package/dist/templates/link-digest/workflows/digest.ts +248 -0
- package/dist/templates/math-buddy/agent.ts +0 -2
- package/dist/templates/night-owl/agent.test.ts +80 -0
- package/dist/templates/night-owl/agent.ts +1 -68
- package/dist/templates/night-owl/system-prompt.md +10 -0
- package/dist/templates/night-owl/tools/recommend.ts +64 -0
- package/dist/templates/personal-finance/agent.ts +0 -2
- package/dist/templates/pizza-ordering/agent.test.ts +59 -58
- package/dist/templates/pizza-ordering/agent.ts +10 -159
- package/dist/templates/pizza-ordering/client.tsx +4 -4
- package/dist/templates/pizza-ordering/shared.ts +44 -27
- package/dist/templates/pizza-ordering/tools/add_pizza.ts +31 -0
- package/dist/templates/pizza-ordering/tools/place_order.ts +22 -0
- package/dist/templates/pizza-ordering/tools/remove_pizza.ts +22 -0
- package/dist/templates/pizza-ordering/tools/set_customer_name.ts +13 -0
- package/dist/templates/pizza-ordering/tools/update_pizza.ts +28 -0
- package/dist/templates/pizza-ordering/tools/view_order.ts +21 -0
- package/dist/templates/plan-and-execute/agent.test.ts +411 -0
- package/dist/templates/plan-and-execute/agent.ts +23 -0
- package/dist/templates/plan-and-execute/client.tsx +115 -0
- package/dist/templates/plan-and-execute/graph.ts +178 -0
- package/dist/templates/plan-and-execute/prompts.ts +110 -0
- package/dist/templates/plan-and-execute/shared.ts +161 -0
- package/dist/templates/plan-and-execute/system-prompt.md +18 -0
- package/dist/templates/plan-and-execute/tools/plan_status.ts +20 -0
- package/dist/templates/plan-and-execute/tools/revise_plan.ts +63 -0
- package/dist/templates/plan-and-execute/tools/start_plan.ts +51 -0
- package/dist/templates/plan-and-execute/tools/work_next_step.ts +101 -0
- package/dist/templates/recap-workflow/agent.test.ts +762 -0
- package/dist/templates/recap-workflow/agent.ts +95 -0
- package/dist/templates/recap-workflow/shared.ts +43 -0
- package/dist/templates/recap-workflow/tools/cancel_recap.ts +23 -0
- package/dist/templates/recap-workflow/tools/keep_transcript.ts +31 -0
- package/dist/templates/recap-workflow/tools/recap_progress.ts +27 -0
- package/dist/templates/recap-workflow/tools/recap_status.ts +46 -0
- package/dist/templates/recap-workflow/tools/request_recap.ts +43 -0
- package/dist/templates/recap-workflow/workflows/recap.ts +544 -0
- package/dist/templates/recap-workflow/workflows/tokens.ts +26 -0
- package/dist/templates/redline/agent.test.ts +222 -0
- package/dist/templates/redline/agent.ts +89 -0
- package/dist/templates/redline/client.tsx +184 -0
- package/dist/templates/redline/workflows/prompts.ts +68 -0
- package/dist/templates/redline/workflows/redline.ts +258 -0
- package/dist/templates/research-workflow/agent.test.ts +525 -0
- package/dist/templates/research-workflow/agent.ts +94 -0
- package/dist/templates/research-workflow/shared.ts +35 -0
- package/dist/templates/research-workflow/tools/file_it_now.ts +22 -0
- package/dist/templates/research-workflow/tools/request_research.ts +33 -0
- package/dist/templates/research-workflow/tools/research_progress.ts +26 -0
- package/dist/templates/research-workflow/tools/research_status.ts +40 -0
- package/dist/templates/research-workflow/workflows/prompts.ts +143 -0
- package/dist/templates/research-workflow/workflows/research.ts +558 -0
- package/dist/templates/retail/agent.test.ts +319 -256
- package/dist/templates/retail/agent.ts +6 -46
- package/dist/templates/retail/authenticate.ts +2 -2
- package/dist/templates/retail/client.tsx +45 -24
- package/dist/templates/retail/registry.test.ts +40 -25
- package/dist/templates/retail/resolve.test.ts +17 -16
- package/dist/templates/retail/resolve.ts +32 -67
- package/dist/templates/retail/shared.test.ts +21 -19
- package/dist/templates/retail/shared.ts +61 -23
- package/dist/templates/retail/store.test.ts +47 -52
- package/dist/templates/retail/store.ts +92 -79
- package/dist/templates/retail/swap.test.ts +20 -17
- package/dist/templates/retail/swap.ts +7 -14
- package/dist/templates/retail/tools/cancel_pending_order.ts +7 -11
- package/dist/templates/retail/tools/exchange_delivered_order_items.ts +26 -14
- package/dist/templates/retail/tools/find_user_id_by_email.ts +5 -9
- package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +5 -7
- package/dist/templates/retail/tools/get_item_details.ts +7 -8
- package/dist/templates/retail/tools/get_order_details.ts +7 -8
- package/dist/templates/retail/tools/get_product_details.ts +6 -8
- package/dist/templates/retail/tools/get_user_details.ts +6 -8
- package/dist/templates/retail/tools/list_all_product_types.ts +5 -7
- package/dist/templates/retail/tools/modify_pending_order_address.ts +6 -10
- package/dist/templates/retail/tools/modify_pending_order_items.ts +8 -20
- package/dist/templates/retail/tools/modify_pending_order_payment.ts +7 -13
- package/dist/templates/retail/tools/modify_user_address.ts +6 -10
- package/dist/templates/retail/tools/return_delivered_order_items.ts +7 -13
- package/dist/templates/retail/tools/transfer_to_human_agents.ts +1 -1
- package/dist/templates/solo-rpg/agent.test.ts +169 -54
- package/dist/templates/solo-rpg/agent.ts +4 -22
- package/dist/templates/solo-rpg/client.tsx +19 -6
- package/dist/templates/solo-rpg/shared.ts +20 -20
- package/dist/templates/solo-rpg/tools/action_roll.ts +3 -5
- package/dist/templates/solo-rpg/tools/burn_momentum.ts +3 -5
- package/dist/templates/solo-rpg/tools/check_state.ts +7 -7
- package/dist/templates/solo-rpg/tools/load_game.ts +3 -3
- package/dist/templates/solo-rpg/tools/oracle.ts +19 -12
- package/dist/templates/solo-rpg/tools/save_game.ts +11 -8
- package/dist/templates/solo-rpg/tools/setup_character.ts +3 -3
- package/dist/templates/solo-rpg/tools/update_state.ts +13 -13
- package/dist/templates/support-line/agent.test.ts +343 -0
- package/dist/templates/support-line/agent.ts +27 -0
- package/dist/templates/support-line/client.tsx +122 -0
- package/dist/templates/support-line/graph.ts +224 -0
- package/dist/templates/support-line/knowledge.json +65 -0
- package/dist/templates/support-line/prompts.ts +95 -0
- package/dist/templates/support-line/shared.ts +240 -0
- package/dist/templates/support-line/system-prompt.md +17 -0
- package/dist/templates/support-line/tools/answer_question.ts +72 -0
- package/dist/templates/support-line/tools/list_topics.ts +22 -0
- package/dist/templates/support-line/tools/log_ticket.ts +30 -0
- package/dist/templates/transcription-workflow/agent.test.ts +563 -0
- package/dist/templates/transcription-workflow/agent.ts +92 -0
- package/dist/templates/transcription-workflow/client.tsx +234 -0
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +416 -0
- package/dist/templates/transcription-workflow/workflows/wav.ts +314 -0
- package/dist/templates/travel-concierge/agent.test.ts +338 -0
- package/dist/templates/travel-concierge/agent.ts +29 -0
- package/dist/templates/travel-concierge/client.tsx +161 -0
- package/dist/templates/travel-concierge/routing.ts +138 -0
- package/dist/templates/travel-concierge/shared.ts +547 -0
- package/dist/templates/travel-concierge/system-prompt.md +24 -0
- package/dist/templates/travel-concierge/tools/book_car_rental.ts +20 -0
- package/dist/templates/travel-concierge/tools/book_excursion.ts +18 -0
- package/dist/templates/travel-concierge/tools/book_hotel.ts +20 -0
- package/dist/templates/travel-concierge/tools/cancel_action.ts +7 -0
- package/dist/templates/travel-concierge/tools/cancel_ticket.ts +16 -0
- package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +7 -0
- package/dist/templates/travel-concierge/tools/confirm_action.ts +7 -0
- package/dist/templates/travel-concierge/tools/lookup_booking.ts +35 -0
- package/dist/templates/travel-concierge/tools/search_car_rentals.ts +33 -0
- package/dist/templates/travel-concierge/tools/search_excursions.ts +37 -0
- package/dist/templates/travel-concierge/tools/search_flights.ts +43 -0
- package/dist/templates/travel-concierge/tools/search_hotels.ts +36 -0
- package/dist/templates/travel-concierge/tools/to_car_rental_assistant.ts +11 -0
- package/dist/templates/travel-concierge/tools/to_excursion_assistant.ts +11 -0
- package/dist/templates/travel-concierge/tools/to_flight_assistant.ts +11 -0
- package/dist/templates/travel-concierge/tools/to_hotel_assistant.ts +11 -0
- package/dist/templates/travel-concierge/tools/update_ticket.ts +22 -0
- package/dist/templates/web-researcher/agent.ts +0 -2
- package/dist/templates/web-researcher/system-prompt.md +10 -0
- package/dist/{test-2LWBcXvY.mjs → test-B6ZBxSpk.mjs} +2 -2
- package/dist/typecheck.mjs +17 -15
- package/dist/worker-bundler-CIdDhZ0c.mjs +398 -0
- package/dist/worker-bundler.d.ts +14 -0
- package/dist/worker-bundler.mjs +1 -108
- package/dist/workflow-B-O97AqI.mjs +125 -0
- package/dist/workflow-bundler.d.ts +133 -0
- package/dist/workflow.d.ts +67 -0
- package/package.json +13 -9
- package/dist/build-DQJ1ZSm9.mjs +0 -39
|
@@ -1,101 +1,116 @@
|
|
|
1
1
|
import type { ToolContext } from "@alexkroman1/aai";
|
|
2
|
+
import { isToolFailure } from "@alexkroman1/aai";
|
|
3
|
+
import { createToolContext } from "@alexkroman1/aai/testing";
|
|
2
4
|
import { describe, expect, test } from "vitest";
|
|
3
5
|
import type { AuthResult } from "./authenticate.ts";
|
|
4
6
|
import type { Address } from "./shared.ts";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
7
|
+
import { retailSlot } from "./store.ts";
|
|
8
|
+
import cancelPendingOrder from "./tools/cancel_pending_order.ts";
|
|
9
|
+
import exchangeDeliveredOrderItems from "./tools/exchange_delivered_order_items.ts";
|
|
10
|
+
import findUserIdByEmail from "./tools/find_user_id_by_email.ts";
|
|
11
|
+
import findUserIdByNameZip from "./tools/find_user_id_by_name_zip.ts";
|
|
12
|
+
import getItemDetails from "./tools/get_item_details.ts";
|
|
13
|
+
import getOrderDetails from "./tools/get_order_details.ts";
|
|
14
|
+
import getProductDetails from "./tools/get_product_details.ts";
|
|
15
|
+
import getUserDetails from "./tools/get_user_details.ts";
|
|
16
|
+
import listAllProductTypes from "./tools/list_all_product_types.ts";
|
|
17
|
+
import modifyPendingOrderAddress from "./tools/modify_pending_order_address.ts";
|
|
18
|
+
import modifyPendingOrderItems from "./tools/modify_pending_order_items.ts";
|
|
19
|
+
import modifyPendingOrderPayment from "./tools/modify_pending_order_payment.ts";
|
|
20
|
+
import modifyUserAddress from "./tools/modify_user_address.ts";
|
|
21
|
+
import returnDeliveredOrderItems from "./tools/return_delivered_order_items.ts";
|
|
22
|
+
import transferToHumanAgents from "./tools/transfer_to_human_agents.ts";
|
|
23
|
+
|
|
24
|
+
/** Each call is its own session, so two contexts are two independent stores —
|
|
25
|
+
* which is what the isolation tests below rest on. */
|
|
25
26
|
function makeCtx(): ToolContext {
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
return createToolContext();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The success half of a tool result, or a thrown failure carrying the tool's
|
|
32
|
+
* own message.
|
|
33
|
+
*
|
|
34
|
+
* `ToolDef["execute"]`'s public signature always returns `unknown` — the wire
|
|
35
|
+
* type is fixed so any tool is assignable to `ToolDef`, whatever its body
|
|
36
|
+
* really returns — so every test that reads a field back off a result needed a
|
|
37
|
+
* cast. There were EIGHTEEN of them here, each the same three lines, which is
|
|
38
|
+
* the shape the repo's guides call a missing typed seam: a concentration of
|
|
39
|
+
* identical casts is one narrowing that belongs in one helper, not a cast per
|
|
40
|
+
* assertion. This is that helper, and it is the file's only cast.
|
|
41
|
+
*
|
|
42
|
+
* It also removes the second half of the boilerplate: a `ToolFailure` fails the
|
|
43
|
+
* test HERE, naming what the tool refused, rather than surfacing three lines
|
|
44
|
+
* later as `undefined` on a field nobody assigned.
|
|
45
|
+
*/
|
|
46
|
+
function ok<T>(result: unknown): T {
|
|
47
|
+
if (isToolFailure(result)) throw new Error(`tool refused: ${result.error}`);
|
|
48
|
+
return result as T;
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
/** A context already authenticated as `userId`, via the real tool. */
|
|
36
52
|
async function authedCtx(email: string): Promise<ToolContext> {
|
|
37
53
|
const ctx = makeCtx();
|
|
38
|
-
|
|
39
|
-
// wire type is fixed so any tool is assignable to `ToolDef`, regardless of
|
|
40
|
-
// what its own `execute` body actually returns), so a cast is needed
|
|
41
|
-
// anywhere a test reads a success field back off the result — matching the
|
|
42
|
-
// `as {...}` convention other templates already use for the same reason.
|
|
43
|
-
const result = (await findUserIdByEmail.execute({ email }, ctx)) as AuthResult | ErrorResult;
|
|
44
|
-
if (isError(result)) throw new Error(`fixture failed to authenticate: ${result.error}`);
|
|
54
|
+
ok<AuthResult>(await findUserIdByEmail.execute({ email }, ctx));
|
|
45
55
|
return ctx;
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
describe("authentication", () => {
|
|
49
59
|
test("find_user_id_by_email is case-insensitive and authenticates the session", async () => {
|
|
50
60
|
const ctx = makeCtx();
|
|
51
|
-
const result = (
|
|
52
|
-
{ email: "OLIVIA.ITO5204@EXAMPLE.COM" },
|
|
53
|
-
|
|
54
|
-
))
|
|
55
|
-
expect(
|
|
56
|
-
expect(getState(ctx).authenticatedUserId).toBe("olivia_ito_3591");
|
|
61
|
+
const result = ok<AuthResult>(
|
|
62
|
+
await findUserIdByEmail.execute({ email: "OLIVIA.ITO5204@EXAMPLE.COM" }, ctx),
|
|
63
|
+
);
|
|
64
|
+
expect(result.user_id).toBe("olivia_ito_3591");
|
|
65
|
+
expect(retailSlot.get(ctx).authenticatedUserId).toBe("olivia_ito_3591");
|
|
57
66
|
});
|
|
58
67
|
|
|
59
68
|
test("an unknown email is refused and leaves the session unauthenticated", async () => {
|
|
60
69
|
const ctx = makeCtx();
|
|
61
70
|
const result = await findUserIdByEmail.execute({ email: "nobody@example.com" }, ctx);
|
|
62
|
-
expect(
|
|
63
|
-
expect(
|
|
71
|
+
expect(isToolFailure(result)).toBe(true);
|
|
72
|
+
expect(retailSlot.get(ctx).authenticatedUserId).toBeNull();
|
|
64
73
|
});
|
|
65
74
|
|
|
66
75
|
test("find_user_id_by_name_zip is case-insensitive on names and exact on zip", async () => {
|
|
67
76
|
const ctx = makeCtx();
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
const found = ok<AuthResult>(
|
|
78
|
+
await findUserIdByNameZip.execute(
|
|
79
|
+
{ first_name: "aarav", last_name: "ANDERSON", zip: "19031" },
|
|
80
|
+
ctx,
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
expect(found.user_id).toBe("aarav_anderson_8794");
|
|
73
84
|
|
|
74
85
|
const wrongZip = await findUserIdByNameZip.execute(
|
|
75
86
|
{ first_name: "Aarav", last_name: "Anderson", zip: "78268" },
|
|
76
87
|
makeCtx(),
|
|
77
88
|
);
|
|
78
|
-
expect(
|
|
89
|
+
expect(isToolFailure(wrongZip)).toBe(true);
|
|
79
90
|
});
|
|
80
91
|
|
|
81
92
|
test("two customers share a first name — the zip is what separates them", async () => {
|
|
82
|
-
const a = (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
const a = ok<AuthResult>(
|
|
94
|
+
await findUserIdByNameZip.execute(
|
|
95
|
+
{ first_name: "Aarav", last_name: "Anderson", zip: "19031" },
|
|
96
|
+
makeCtx(),
|
|
97
|
+
),
|
|
98
|
+
);
|
|
99
|
+
const b = ok<AuthResult>(
|
|
100
|
+
await findUserIdByNameZip.execute(
|
|
101
|
+
{ first_name: "Aarav", last_name: "Gonzalez", zip: "78268" },
|
|
102
|
+
makeCtx(),
|
|
103
|
+
),
|
|
104
|
+
);
|
|
105
|
+
expect(a.user_id).toBe("aarav_anderson_8794");
|
|
106
|
+
expect(b.user_id).toBe("aarav_gonzalez_5113");
|
|
92
107
|
});
|
|
93
108
|
|
|
94
109
|
test("re-authenticating as the same customer is a no-op success", async () => {
|
|
95
110
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
96
111
|
const again = await findUserIdByEmail.execute({ email: "olivia.ito5204@example.com" }, ctx);
|
|
97
|
-
expect(
|
|
98
|
-
expect(
|
|
112
|
+
expect(isToolFailure(again)).toBe(false);
|
|
113
|
+
expect(retailSlot.get(ctx).authenticatedUserId).toBe("olivia_ito_3591");
|
|
99
114
|
});
|
|
100
115
|
|
|
101
116
|
test("switching to a DIFFERENT customer mid-conversation is refused", async () => {
|
|
@@ -104,8 +119,8 @@ describe("authentication", () => {
|
|
|
104
119
|
{ email: "aarav.anderson9752@example.com" },
|
|
105
120
|
ctx,
|
|
106
121
|
);
|
|
107
|
-
expect(
|
|
108
|
-
expect(
|
|
122
|
+
expect(isToolFailure(switched) && switched.error.toLowerCase()).toContain("one customer");
|
|
123
|
+
expect(retailSlot.get(ctx).authenticatedUserId).toBe("olivia_ito_3591");
|
|
109
124
|
});
|
|
110
125
|
|
|
111
126
|
test("switching via name + zip is refused too — both doors, one lock", async () => {
|
|
@@ -114,8 +129,8 @@ describe("authentication", () => {
|
|
|
114
129
|
{ first_name: "Aarav", last_name: "Anderson", zip: "19031" },
|
|
115
130
|
ctx,
|
|
116
131
|
);
|
|
117
|
-
expect(
|
|
118
|
-
expect(
|
|
132
|
+
expect(isToolFailure(switched)).toBe(true);
|
|
133
|
+
expect(retailSlot.get(ctx).authenticatedUserId).toBe("olivia_ito_3591");
|
|
119
134
|
});
|
|
120
135
|
});
|
|
121
136
|
|
|
@@ -147,10 +162,9 @@ interface ProductTypesResult {
|
|
|
147
162
|
describe("read tools", () => {
|
|
148
163
|
test("get_user_details returns the caller's profile with gift-card balances", async () => {
|
|
149
164
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
150
|
-
const result = (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (isError(result)) throw new Error(result.error);
|
|
165
|
+
const result = ok<UserDetailsResult>(
|
|
166
|
+
await getUserDetails.execute({ user_id: "olivia_ito_3591" }, ctx),
|
|
167
|
+
);
|
|
154
168
|
expect(result.email).toBe("olivia.ito5204@example.com");
|
|
155
169
|
expect(result.orders).toHaveLength(5);
|
|
156
170
|
const card = result.payment_methods.find((m) => m.payment_method_id === "gift_card_7794233");
|
|
@@ -160,30 +174,28 @@ describe("read tools", () => {
|
|
|
160
174
|
test("get_user_details refuses a different user id", async () => {
|
|
161
175
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
162
176
|
const result = await getUserDetails.execute({ user_id: "aarav_anderson_8794" }, ctx);
|
|
163
|
-
expect(
|
|
177
|
+
expect(isToolFailure(result)).toBe(true);
|
|
164
178
|
});
|
|
165
179
|
|
|
166
180
|
test("get_order_details resolves shorthand and sets focus", async () => {
|
|
167
181
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
168
|
-
const result = (
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (isError(result)) throw new Error(result.error);
|
|
182
|
+
const result = ok<OrderDetailsResult>(
|
|
183
|
+
await getOrderDetails.execute({ order_id: "the delivered one" }, ctx),
|
|
184
|
+
);
|
|
172
185
|
expect(result.order_id).toBe("#W5866402");
|
|
173
|
-
expect(
|
|
186
|
+
expect(retailSlot.get(ctx).focus.orderId).toBe("#W5866402");
|
|
174
187
|
});
|
|
175
188
|
|
|
176
189
|
test("get_order_details refuses another customer's order", async () => {
|
|
177
190
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
178
191
|
const result = await getOrderDetails.execute({ order_id: "#W4316152" }, ctx);
|
|
179
|
-
expect(
|
|
192
|
+
expect(isToolFailure(result)).toBe(true);
|
|
180
193
|
});
|
|
181
194
|
|
|
182
195
|
test("get_product_details lists variants and needs no authentication", async () => {
|
|
183
|
-
const result = (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (isError(result)) throw new Error(result.error);
|
|
196
|
+
const result = ok<ProductDetailsResult>(
|
|
197
|
+
await getProductDetails.execute({ product_id: "9832717871" }, makeCtx()),
|
|
198
|
+
);
|
|
187
199
|
expect(result.name).toBe("Tea Kettle");
|
|
188
200
|
expect(result.variants.length).toBeGreaterThan(1);
|
|
189
201
|
expect(result.variants[0]).toHaveProperty("item_id");
|
|
@@ -191,23 +203,19 @@ describe("read tools", () => {
|
|
|
191
203
|
|
|
192
204
|
test("get_product_details rejects an item id passed as a product id, and says so", async () => {
|
|
193
205
|
const result = await getProductDetails.execute({ product_id: "3909406921" }, makeCtx());
|
|
194
|
-
expect(
|
|
206
|
+
expect(isToolFailure(result) && result.error).toContain("item id");
|
|
195
207
|
});
|
|
196
208
|
|
|
197
209
|
test("get_item_details resolves an item without knowing its product", async () => {
|
|
198
|
-
const result = (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (isError(result)) throw new Error(result.error);
|
|
210
|
+
const result = ok<ItemDetailsResult>(
|
|
211
|
+
await getItemDetails.execute({ item_id: "3909406921" }, makeCtx()),
|
|
212
|
+
);
|
|
202
213
|
expect(result.price).toBe(98.25);
|
|
203
214
|
expect(result.product_name).toBe("Tea Kettle");
|
|
204
215
|
});
|
|
205
216
|
|
|
206
217
|
test("list_all_product_types returns all 50, sorted by name", async () => {
|
|
207
|
-
const result = (await listAllProductTypes.execute({}, makeCtx()))
|
|
208
|
-
| ProductTypesResult
|
|
209
|
-
| ErrorResult;
|
|
210
|
-
if (isError(result)) throw new Error(result.error);
|
|
218
|
+
const result = ok<ProductTypesResult>(await listAllProductTypes.execute({}, makeCtx()));
|
|
211
219
|
expect(Object.keys(result.products)).toHaveLength(50);
|
|
212
220
|
const names = Object.keys(result.products);
|
|
213
221
|
// localeCompare, not the default lexicographic sort — the tool sorts for a
|
|
@@ -229,15 +237,16 @@ interface CancelOrderResult {
|
|
|
229
237
|
describe("cancel_pending_order", () => {
|
|
230
238
|
test("cancels a pending order and credits a gift card immediately", async () => {
|
|
231
239
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
232
|
-
const result = (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
240
|
+
const result = ok<CancelOrderResult>(
|
|
241
|
+
await cancelPendingOrder.execute(
|
|
242
|
+
{ order_id: "#W9300146", reason: "ordered by mistake" },
|
|
243
|
+
ctx,
|
|
244
|
+
),
|
|
245
|
+
);
|
|
237
246
|
expect(result.status).toBe("cancelled");
|
|
238
247
|
expect(result.refund_immediate).toBe(true);
|
|
239
248
|
|
|
240
|
-
const state =
|
|
249
|
+
const state = retailSlot.get(ctx);
|
|
241
250
|
expect(state.store.orders["#W9300146"]?.status).toBe("cancelled");
|
|
242
251
|
expect(state.store.orders["#W9300146"]?.cancel_reason).toBe("ordered by mistake");
|
|
243
252
|
// $17.00 card + the $153.23 order total.
|
|
@@ -248,7 +257,7 @@ describe("cancel_pending_order", () => {
|
|
|
248
257
|
test("appends a matching refund to the payment history", async () => {
|
|
249
258
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
250
259
|
await cancelPendingOrder.execute({ order_id: "#W9300146", reason: "no longer needed" }, ctx);
|
|
251
|
-
const history =
|
|
260
|
+
const history = retailSlot.get(ctx).store.orders["#W9300146"]?.payment_history ?? [];
|
|
252
261
|
expect(history).toHaveLength(2);
|
|
253
262
|
expect(history[1]).toMatchObject({
|
|
254
263
|
transaction_type: "refund",
|
|
@@ -259,14 +268,12 @@ describe("cancel_pending_order", () => {
|
|
|
259
268
|
|
|
260
269
|
test("a non-gift-card refund takes 5-7 days and moves no balance", async () => {
|
|
261
270
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
262
|
-
const result = (
|
|
263
|
-
{ order_id: "#W5442520", reason: "no longer needed" },
|
|
264
|
-
|
|
265
|
-
)) as CancelOrderResult | ErrorResult;
|
|
266
|
-
if (isError(result)) throw new Error(result.error);
|
|
271
|
+
const result = ok<CancelOrderResult>(
|
|
272
|
+
await cancelPendingOrder.execute({ order_id: "#W5442520", reason: "no longer needed" }, ctx),
|
|
273
|
+
);
|
|
267
274
|
expect(result.refund_immediate).toBe(false);
|
|
268
275
|
expect(result.message).toContain("5 to 7");
|
|
269
|
-
const card =
|
|
276
|
+
const card = retailSlot.get(ctx).store.users.olivia_ito_3591?.payment_methods.gift_card_7794233;
|
|
270
277
|
expect(card?.source === "gift_card" && card.balance).toBe(56);
|
|
271
278
|
});
|
|
272
279
|
|
|
@@ -276,8 +283,8 @@ describe("cancel_pending_order", () => {
|
|
|
276
283
|
{ order_id: "#W5353646", reason: "no longer needed" },
|
|
277
284
|
ctx,
|
|
278
285
|
);
|
|
279
|
-
expect(
|
|
280
|
-
expect(
|
|
286
|
+
expect(isToolFailure(result) && result.error).toContain("processed");
|
|
287
|
+
expect(retailSlot.get(ctx).store.orders["#W5353646"]?.status).toBe("processed");
|
|
281
288
|
});
|
|
282
289
|
|
|
283
290
|
test("refuses a delivered order", async () => {
|
|
@@ -286,7 +293,7 @@ describe("cancel_pending_order", () => {
|
|
|
286
293
|
{ order_id: "#W5866402", reason: "no longer needed" },
|
|
287
294
|
ctx,
|
|
288
295
|
);
|
|
289
|
-
expect(
|
|
296
|
+
expect(isToolFailure(result)).toBe(true);
|
|
290
297
|
});
|
|
291
298
|
|
|
292
299
|
test("refuses a reason outside tau2's two accepted values", async () => {
|
|
@@ -298,8 +305,8 @@ describe("cancel_pending_order", () => {
|
|
|
298
305
|
{ order_id: "#W9300146", reason: "changed my mind" as unknown as "no longer needed" },
|
|
299
306
|
ctx,
|
|
300
307
|
);
|
|
301
|
-
expect(
|
|
302
|
-
expect(
|
|
308
|
+
expect(isToolFailure(result) && result.error).toContain("no longer needed");
|
|
309
|
+
expect(retailSlot.get(ctx).store.orders["#W9300146"]?.status).toBe("pending");
|
|
303
310
|
});
|
|
304
311
|
|
|
305
312
|
test("refuses cancelling twice", async () => {
|
|
@@ -309,18 +316,21 @@ describe("cancel_pending_order", () => {
|
|
|
309
316
|
{ order_id: "#W9300146", reason: "no longer needed" },
|
|
310
317
|
ctx,
|
|
311
318
|
);
|
|
312
|
-
expect(
|
|
313
|
-
const card =
|
|
319
|
+
expect(isToolFailure(second)).toBe(true);
|
|
320
|
+
const card =
|
|
321
|
+
retailSlot.get(ctx).store.users.aarav_anderson_8794?.payment_methods.gift_card_7245904;
|
|
314
322
|
expect(card?.source === "gift_card" && card.balance).toBe(170.23);
|
|
315
323
|
});
|
|
316
324
|
|
|
317
325
|
test("resolves spoken shorthand to the single pending order", async () => {
|
|
318
326
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
319
|
-
const result = (
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
327
|
+
const result = ok<CancelOrderResult>(
|
|
328
|
+
await cancelPendingOrder.execute(
|
|
329
|
+
{ order_id: "my pending order", reason: "ordered by mistake" },
|
|
330
|
+
ctx,
|
|
331
|
+
),
|
|
332
|
+
);
|
|
333
|
+
expect(result.order_id).toBe("#W9300146");
|
|
324
334
|
});
|
|
325
335
|
|
|
326
336
|
test("refuses ambiguous shorthand rather than cancelling the wrong order", async () => {
|
|
@@ -329,9 +339,9 @@ describe("cancel_pending_order", () => {
|
|
|
329
339
|
{ order_id: "my pending order", reason: "no longer needed" },
|
|
330
340
|
ctx,
|
|
331
341
|
);
|
|
332
|
-
expect(
|
|
342
|
+
expect(isToolFailure(result)).toBe(true);
|
|
333
343
|
const statuses = ["#W5442520", "#W7941031", "#W3657213"].map(
|
|
334
|
-
(id) =>
|
|
344
|
+
(id) => retailSlot.get(ctx).store.orders[id]?.status,
|
|
335
345
|
);
|
|
336
346
|
expect(statuses).toEqual(["pending", "pending", "pending"]);
|
|
337
347
|
});
|
|
@@ -354,31 +364,31 @@ interface AddressToolResult {
|
|
|
354
364
|
describe("modify_pending_order_address", () => {
|
|
355
365
|
test("rewrites a pending order's shipping address", async () => {
|
|
356
366
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
357
|
-
const result = (
|
|
358
|
-
{ order_id: "#W9300146", ...NEW_ADDRESS },
|
|
359
|
-
|
|
360
|
-
)) as AddressToolResult | ErrorResult;
|
|
361
|
-
if (isError(result)) throw new Error(result.error);
|
|
367
|
+
const result = ok<AddressToolResult>(
|
|
368
|
+
await modifyPendingOrderAddress.execute({ order_id: "#W9300146", ...NEW_ADDRESS }, ctx),
|
|
369
|
+
);
|
|
362
370
|
expect(result.address).toEqual(NEW_ADDRESS);
|
|
363
|
-
expect(
|
|
371
|
+
expect(retailSlot.get(ctx).store.orders["#W9300146"]?.address).toEqual(NEW_ADDRESS);
|
|
364
372
|
});
|
|
365
373
|
|
|
366
374
|
test("leaves the customer's default address untouched", async () => {
|
|
367
375
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
368
376
|
await modifyPendingOrderAddress.execute({ order_id: "#W9300146", ...NEW_ADDRESS }, ctx);
|
|
369
|
-
expect(
|
|
377
|
+
expect(retailSlot.get(ctx).store.users.aarav_anderson_8794?.address.zip).toBe("19031");
|
|
370
378
|
});
|
|
371
379
|
|
|
372
380
|
test("accepts a 'pending (item modified)' order — unlike cancel", async () => {
|
|
373
381
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
382
|
+
retailSlot.update(ctx, (state) => {
|
|
383
|
+
const order = state.store.orders["#W9300146"];
|
|
384
|
+
if (!order) throw new Error("fixture missing");
|
|
385
|
+
order.status = "pending (item modified)";
|
|
386
|
+
});
|
|
377
387
|
const result = await modifyPendingOrderAddress.execute(
|
|
378
388
|
{ order_id: "#W9300146", ...NEW_ADDRESS },
|
|
379
389
|
ctx,
|
|
380
390
|
);
|
|
381
|
-
expect(
|
|
391
|
+
expect(isToolFailure(result)).toBe(false);
|
|
382
392
|
});
|
|
383
393
|
|
|
384
394
|
test("refuses a delivered order", async () => {
|
|
@@ -387,7 +397,7 @@ describe("modify_pending_order_address", () => {
|
|
|
387
397
|
{ order_id: "#W5866402", ...NEW_ADDRESS },
|
|
388
398
|
ctx,
|
|
389
399
|
);
|
|
390
|
-
expect(
|
|
400
|
+
expect(isToolFailure(result)).toBe(true);
|
|
391
401
|
});
|
|
392
402
|
|
|
393
403
|
test("refuses another customer's order", async () => {
|
|
@@ -396,26 +406,22 @@ describe("modify_pending_order_address", () => {
|
|
|
396
406
|
{ order_id: "#W9300146", ...NEW_ADDRESS },
|
|
397
407
|
ctx,
|
|
398
408
|
);
|
|
399
|
-
expect(
|
|
409
|
+
expect(isToolFailure(result)).toBe(true);
|
|
400
410
|
});
|
|
401
411
|
});
|
|
402
412
|
|
|
403
413
|
describe("modify_user_address", () => {
|
|
404
414
|
test("rewrites the customer's default address", async () => {
|
|
405
415
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
ctx,
|
|
409
|
-
);
|
|
410
|
-
if (isError(result)) throw new Error(result.error);
|
|
411
|
-
expect(getState(ctx).store.users.emma_smith_8564?.address).toEqual(NEW_ADDRESS);
|
|
416
|
+
ok(await modifyUserAddress.execute({ user_id: "emma_smith_8564", ...NEW_ADDRESS }, ctx));
|
|
417
|
+
expect(retailSlot.get(ctx).store.users.emma_smith_8564?.address).toEqual(NEW_ADDRESS);
|
|
412
418
|
});
|
|
413
419
|
|
|
414
420
|
test("leaves existing orders' addresses untouched", async () => {
|
|
415
421
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
416
|
-
const before = structuredClone(
|
|
422
|
+
const before = structuredClone(retailSlot.get(ctx).store.orders["#W2417020"]?.address);
|
|
417
423
|
await modifyUserAddress.execute({ user_id: "emma_smith_8564", ...NEW_ADDRESS }, ctx);
|
|
418
|
-
expect(
|
|
424
|
+
expect(retailSlot.get(ctx).store.orders["#W2417020"]?.address).toEqual(before);
|
|
419
425
|
});
|
|
420
426
|
|
|
421
427
|
test("refuses a different user id", async () => {
|
|
@@ -424,8 +430,8 @@ describe("modify_user_address", () => {
|
|
|
424
430
|
{ user_id: "olivia_ito_3591", ...NEW_ADDRESS },
|
|
425
431
|
ctx,
|
|
426
432
|
);
|
|
427
|
-
expect(
|
|
428
|
-
expect(
|
|
433
|
+
expect(isToolFailure(result)).toBe(true);
|
|
434
|
+
expect(retailSlot.get(ctx).store.users.olivia_ito_3591?.address.zip).toBe("80218");
|
|
429
435
|
});
|
|
430
436
|
|
|
431
437
|
test("requires authentication", async () => {
|
|
@@ -433,7 +439,7 @@ describe("modify_user_address", () => {
|
|
|
433
439
|
{ user_id: "emma_smith_8564", ...NEW_ADDRESS },
|
|
434
440
|
makeCtx(),
|
|
435
441
|
);
|
|
436
|
-
expect(
|
|
442
|
+
expect(isToolFailure(result) && result.error).toContain("find_user_id_by_email");
|
|
437
443
|
});
|
|
438
444
|
});
|
|
439
445
|
|
|
@@ -448,20 +454,21 @@ interface ModifyItemsResult {
|
|
|
448
454
|
describe("modify_pending_order_items", () => {
|
|
449
455
|
test("swaps an item, charges the difference to a gift card, and goes terminal", async () => {
|
|
450
456
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
451
|
-
const result = (
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
457
|
+
const result = ok<ModifyItemsResult>(
|
|
458
|
+
await modifyPendingOrderItems.execute(
|
|
459
|
+
{
|
|
460
|
+
order_id: "#W2417020",
|
|
461
|
+
item_ids: ["8997785118"],
|
|
462
|
+
new_item_ids: ["6017636844"],
|
|
463
|
+
payment_method_id: "gift_card_8541487",
|
|
464
|
+
},
|
|
465
|
+
ctx,
|
|
466
|
+
),
|
|
467
|
+
);
|
|
461
468
|
expect(result.price_difference).toBe(-382.03);
|
|
462
469
|
expect(result.status).toBe("pending (item modified)");
|
|
463
470
|
|
|
464
|
-
const state =
|
|
471
|
+
const state = retailSlot.get(ctx);
|
|
465
472
|
const order = state.store.orders["#W2417020"];
|
|
466
473
|
expect(order?.items[0]?.item_id).toBe("6017636844");
|
|
467
474
|
expect(order?.items[0]?.price).toBe(2292.37);
|
|
@@ -481,7 +488,7 @@ describe("modify_pending_order_items", () => {
|
|
|
481
488
|
},
|
|
482
489
|
ctx,
|
|
483
490
|
);
|
|
484
|
-
const history =
|
|
491
|
+
const history = retailSlot.get(ctx).store.orders["#W2417020"]?.payment_history ?? [];
|
|
485
492
|
expect(history[1]).toMatchObject({
|
|
486
493
|
transaction_type: "refund",
|
|
487
494
|
amount: 382.03,
|
|
@@ -509,7 +516,7 @@ describe("modify_pending_order_items", () => {
|
|
|
509
516
|
},
|
|
510
517
|
ctx,
|
|
511
518
|
);
|
|
512
|
-
expect(
|
|
519
|
+
expect(isToolFailure(second) && second.error).toContain("pending (item modified)");
|
|
513
520
|
});
|
|
514
521
|
|
|
515
522
|
test("a modified order can no longer be cancelled", async () => {
|
|
@@ -527,7 +534,7 @@ describe("modify_pending_order_items", () => {
|
|
|
527
534
|
{ order_id: "#W2417020", reason: "no longer needed" },
|
|
528
535
|
ctx,
|
|
529
536
|
);
|
|
530
|
-
expect(
|
|
537
|
+
expect(isToolFailure(cancelled)).toBe(true);
|
|
531
538
|
});
|
|
532
539
|
|
|
533
540
|
test("refuses when the gift card cannot cover the difference, changing nothing", async () => {
|
|
@@ -545,9 +552,9 @@ describe("modify_pending_order_items", () => {
|
|
|
545
552
|
},
|
|
546
553
|
ctx,
|
|
547
554
|
);
|
|
548
|
-
expect(
|
|
555
|
+
expect(isToolFailure(result) && result.error.toLowerCase()).toContain("balance");
|
|
549
556
|
|
|
550
|
-
const state =
|
|
557
|
+
const state = retailSlot.get(ctx);
|
|
551
558
|
const order = state.store.orders["#W6436609"];
|
|
552
559
|
expect(order?.status).toBe("pending");
|
|
553
560
|
expect(order?.items.map((i) => i.item_id)).toContain("6017636844");
|
|
@@ -558,18 +565,19 @@ describe("modify_pending_order_items", () => {
|
|
|
558
565
|
|
|
559
566
|
test("the same upgrade succeeds on a payment method with no balance to run out of", async () => {
|
|
560
567
|
const ctx = await authedCtx("anya.garcia2061@example.com");
|
|
561
|
-
const result = (
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
568
|
+
const result = ok<ModifyItemsResult>(
|
|
569
|
+
await modifyPendingOrderItems.execute(
|
|
570
|
+
{
|
|
571
|
+
order_id: "#W6436609",
|
|
572
|
+
item_ids: ["6017636844"],
|
|
573
|
+
new_item_ids: ["9844888101"],
|
|
574
|
+
payment_method_id: "credit_card_8955149",
|
|
575
|
+
},
|
|
576
|
+
ctx,
|
|
577
|
+
),
|
|
578
|
+
);
|
|
571
579
|
expect(result.price_difference).toBe(167.37);
|
|
572
|
-
const history =
|
|
580
|
+
const history = retailSlot.get(ctx).store.orders["#W6436609"]?.payment_history ?? [];
|
|
573
581
|
expect(history[1]).toMatchObject({
|
|
574
582
|
transaction_type: "payment",
|
|
575
583
|
amount: 167.37,
|
|
@@ -588,7 +596,7 @@ describe("modify_pending_order_items", () => {
|
|
|
588
596
|
},
|
|
589
597
|
ctx,
|
|
590
598
|
);
|
|
591
|
-
expect(
|
|
599
|
+
expect(isToolFailure(result)).toBe(true);
|
|
592
600
|
});
|
|
593
601
|
|
|
594
602
|
test("refuses a payment method belonging to another customer", async () => {
|
|
@@ -602,8 +610,8 @@ describe("modify_pending_order_items", () => {
|
|
|
602
610
|
},
|
|
603
611
|
ctx,
|
|
604
612
|
);
|
|
605
|
-
expect(
|
|
606
|
-
expect(
|
|
613
|
+
expect(isToolFailure(result)).toBe(true);
|
|
614
|
+
expect(retailSlot.get(ctx).store.orders["#W2417020"]?.status).toBe("pending");
|
|
607
615
|
});
|
|
608
616
|
});
|
|
609
617
|
|
|
@@ -620,14 +628,15 @@ describe("modify_pending_order_payment", () => {
|
|
|
620
628
|
test("moves an order to a different method, refunding the old one", async () => {
|
|
621
629
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
622
630
|
// #W5442520 is $663.85 on credit_card_9753331. Pay by PayPal instead.
|
|
623
|
-
const result = (
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
631
|
+
const result = ok<ModifyPaymentResult>(
|
|
632
|
+
await modifyPendingOrderPayment.execute(
|
|
633
|
+
{ order_id: "#W5442520", payment_method_id: "paypal_8049766" },
|
|
634
|
+
ctx,
|
|
635
|
+
),
|
|
636
|
+
);
|
|
628
637
|
expect(result.status).toBe("pending");
|
|
629
638
|
|
|
630
|
-
const history =
|
|
639
|
+
const history = retailSlot.get(ctx).store.orders["#W5442520"]?.payment_history ?? [];
|
|
631
640
|
expect(history).toHaveLength(3);
|
|
632
641
|
expect(history[1]).toMatchObject({
|
|
633
642
|
transaction_type: "payment",
|
|
@@ -648,8 +657,9 @@ describe("modify_pending_order_payment", () => {
|
|
|
648
657
|
{ order_id: "#W9160732", payment_method_id: "paypal_6121064" },
|
|
649
658
|
ctx,
|
|
650
659
|
);
|
|
651
|
-
expect(
|
|
652
|
-
const card =
|
|
660
|
+
expect(isToolFailure(result)).toBe(false);
|
|
661
|
+
const card =
|
|
662
|
+
retailSlot.get(ctx).store.users.aarav_gonzalez_5113?.payment_methods.gift_card_5979071;
|
|
653
663
|
expect(card?.source === "gift_card" && card.balance).toBe(1107.54);
|
|
654
664
|
});
|
|
655
665
|
|
|
@@ -660,10 +670,11 @@ describe("modify_pending_order_payment", () => {
|
|
|
660
670
|
{ order_id: "#W6979932", payment_method_id: "gift_card_5979071" },
|
|
661
671
|
ctx,
|
|
662
672
|
);
|
|
663
|
-
expect(
|
|
664
|
-
const card =
|
|
673
|
+
expect(isToolFailure(result) && result.error.toLowerCase()).toContain("balance");
|
|
674
|
+
const card =
|
|
675
|
+
retailSlot.get(ctx).store.users.aarav_gonzalez_5113?.payment_methods.gift_card_5979071;
|
|
665
676
|
expect(card?.source === "gift_card" && card.balance).toBe(96);
|
|
666
|
-
expect(
|
|
677
|
+
expect(retailSlot.get(ctx).store.orders["#W6979932"]?.payment_history).toHaveLength(1);
|
|
667
678
|
});
|
|
668
679
|
|
|
669
680
|
test("refuses the method the order already uses", async () => {
|
|
@@ -672,25 +683,27 @@ describe("modify_pending_order_payment", () => {
|
|
|
672
683
|
{ order_id: "#W5442520", payment_method_id: "credit_card_9753331" },
|
|
673
684
|
ctx,
|
|
674
685
|
);
|
|
675
|
-
expect(
|
|
686
|
+
expect(isToolFailure(result) && result.error.toLowerCase()).toContain("different");
|
|
676
687
|
});
|
|
677
688
|
|
|
678
689
|
test("refuses an order whose payment history is not a single payment", async () => {
|
|
679
690
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
680
|
-
const order = getState(ctx).store.orders["#W5442520"];
|
|
681
|
-
if (!order) throw new Error("fixture missing");
|
|
682
691
|
// No seeded pending order has a second history entry, so construct one —
|
|
683
692
|
// tau2 guards this case and the guard should still be covered.
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
693
|
+
retailSlot.update(ctx, (state) => {
|
|
694
|
+
const order = state.store.orders["#W5442520"];
|
|
695
|
+
if (!order) throw new Error("fixture missing");
|
|
696
|
+
order.payment_history.push({
|
|
697
|
+
transaction_type: "refund",
|
|
698
|
+
amount: 10,
|
|
699
|
+
payment_method_id: "credit_card_9753331",
|
|
700
|
+
});
|
|
688
701
|
});
|
|
689
702
|
const result = await modifyPendingOrderPayment.execute(
|
|
690
703
|
{ order_id: "#W5442520", payment_method_id: "paypal_8049766" },
|
|
691
704
|
ctx,
|
|
692
705
|
);
|
|
693
|
-
expect(
|
|
706
|
+
expect(isToolFailure(result) && result.error.toLowerCase()).toContain("exactly one payment");
|
|
694
707
|
});
|
|
695
708
|
|
|
696
709
|
test("refuses a delivered order", async () => {
|
|
@@ -699,7 +712,7 @@ describe("modify_pending_order_payment", () => {
|
|
|
699
712
|
{ order_id: "#W5866402", payment_method_id: "gift_card_7794233" },
|
|
700
713
|
ctx,
|
|
701
714
|
);
|
|
702
|
-
expect(
|
|
715
|
+
expect(isToolFailure(result)).toBe(true);
|
|
703
716
|
});
|
|
704
717
|
|
|
705
718
|
test("refuses a method not on the customer's profile", async () => {
|
|
@@ -708,7 +721,7 @@ describe("modify_pending_order_payment", () => {
|
|
|
708
721
|
{ order_id: "#W5442520", payment_method_id: "gift_card_7245904" },
|
|
709
722
|
ctx,
|
|
710
723
|
);
|
|
711
|
-
expect(
|
|
724
|
+
expect(isToolFailure(result)).toBe(true);
|
|
712
725
|
});
|
|
713
726
|
});
|
|
714
727
|
|
|
@@ -724,25 +737,25 @@ interface ExchangeResult {
|
|
|
724
737
|
order_id: string;
|
|
725
738
|
status: string;
|
|
726
739
|
price_difference: number;
|
|
727
|
-
|
|
728
|
-
exchange_new_items: string[];
|
|
740
|
+
exchanges: { item_id: string; new_item_id: string; price_difference: number }[];
|
|
729
741
|
message: string;
|
|
730
742
|
}
|
|
731
743
|
|
|
732
744
|
describe("return_delivered_order_items", () => {
|
|
733
745
|
test("requests a return refunded to the original method", async () => {
|
|
734
746
|
const ctx = await authedCtx("harper.brown3965@example.com");
|
|
735
|
-
const result = (
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
747
|
+
const result = ok<ReturnResult>(
|
|
748
|
+
await returnDeliveredOrderItems.execute(
|
|
749
|
+
{
|
|
750
|
+
order_id: "#W1840144",
|
|
751
|
+
item_ids: ["8590708195"],
|
|
752
|
+
payment_method_id: "paypal_2306935",
|
|
753
|
+
},
|
|
754
|
+
ctx,
|
|
755
|
+
),
|
|
756
|
+
);
|
|
744
757
|
expect(result.status).toBe("return requested");
|
|
745
|
-
const order =
|
|
758
|
+
const order = retailSlot.get(ctx).store.orders["#W1840144"];
|
|
746
759
|
expect(order?.return_items).toEqual(["8590708195"]);
|
|
747
760
|
expect(order?.return_payment_method_id).toBe("paypal_2306935");
|
|
748
761
|
});
|
|
@@ -758,7 +771,7 @@ describe("return_delivered_order_items", () => {
|
|
|
758
771
|
},
|
|
759
772
|
ctx,
|
|
760
773
|
);
|
|
761
|
-
expect(
|
|
774
|
+
expect(isToolFailure(result)).toBe(false);
|
|
762
775
|
});
|
|
763
776
|
|
|
764
777
|
test("refuses a non-original, non-gift-card method", async () => {
|
|
@@ -772,8 +785,8 @@ describe("return_delivered_order_items", () => {
|
|
|
772
785
|
},
|
|
773
786
|
ctx,
|
|
774
787
|
);
|
|
775
|
-
expect(
|
|
776
|
-
expect(
|
|
788
|
+
expect(isToolFailure(result) && result.error.toLowerCase()).toContain("original");
|
|
789
|
+
expect(retailSlot.get(ctx).store.orders["#W1840144"]?.status).toBe("delivered");
|
|
777
790
|
});
|
|
778
791
|
|
|
779
792
|
test("returns both copies of a duplicate item and sorts the recorded list", async () => {
|
|
@@ -786,8 +799,8 @@ describe("return_delivered_order_items", () => {
|
|
|
786
799
|
},
|
|
787
800
|
ctx,
|
|
788
801
|
);
|
|
789
|
-
expect(
|
|
790
|
-
expect(
|
|
802
|
+
expect(isToolFailure(result)).toBe(false);
|
|
803
|
+
expect(retailSlot.get(ctx).store.orders["#W1840144"]?.return_items).toEqual([
|
|
791
804
|
"6534134392",
|
|
792
805
|
"8590708195",
|
|
793
806
|
"8590708195",
|
|
@@ -804,7 +817,7 @@ describe("return_delivered_order_items", () => {
|
|
|
804
817
|
},
|
|
805
818
|
ctx,
|
|
806
819
|
);
|
|
807
|
-
expect(
|
|
820
|
+
expect(isToolFailure(result)).toBe(true);
|
|
808
821
|
});
|
|
809
822
|
|
|
810
823
|
test("refuses a pending order and refuses a second return", async () => {
|
|
@@ -817,7 +830,7 @@ describe("return_delivered_order_items", () => {
|
|
|
817
830
|
},
|
|
818
831
|
ctx,
|
|
819
832
|
);
|
|
820
|
-
expect(
|
|
833
|
+
expect(isToolFailure(pending)).toBe(true);
|
|
821
834
|
|
|
822
835
|
await returnDeliveredOrderItems.execute(
|
|
823
836
|
{ order_id: "#W1840144", item_ids: ["8590708195"], payment_method_id: "paypal_2306935" },
|
|
@@ -827,31 +840,74 @@ describe("return_delivered_order_items", () => {
|
|
|
827
840
|
{ order_id: "#W1840144", item_ids: ["6534134392"], payment_method_id: "paypal_2306935" },
|
|
828
841
|
ctx,
|
|
829
842
|
);
|
|
830
|
-
expect(
|
|
843
|
+
expect(isToolFailure(again) && again.error).toContain("return requested");
|
|
831
844
|
});
|
|
832
845
|
});
|
|
833
846
|
|
|
834
847
|
describe("exchange_delivered_order_items", () => {
|
|
835
848
|
test("records an exchange with a positive price difference", async () => {
|
|
836
849
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
837
|
-
const result = (
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
850
|
+
const result = ok<ExchangeResult>(
|
|
851
|
+
await exchangeDeliveredOrderItems.execute(
|
|
852
|
+
{
|
|
853
|
+
order_id: "#W4316152",
|
|
854
|
+
item_ids: ["7292993796"],
|
|
855
|
+
new_item_ids: ["3909406921"],
|
|
856
|
+
payment_method_id: "gift_card_7245904",
|
|
857
|
+
},
|
|
858
|
+
ctx,
|
|
859
|
+
),
|
|
860
|
+
);
|
|
847
861
|
expect(result.price_difference).toBe(3.45);
|
|
848
|
-
|
|
862
|
+
expect(result.exchanges).toEqual([
|
|
863
|
+
{ item_id: "7292993796", new_item_id: "3909406921", price_difference: 3.45 },
|
|
864
|
+
]);
|
|
865
|
+
const order = retailSlot.get(ctx).store.orders["#W4316152"];
|
|
849
866
|
expect(order?.status).toBe("exchange requested");
|
|
850
867
|
expect(order?.exchange_items).toEqual(["7292993796"]);
|
|
851
868
|
expect(order?.exchange_new_items).toEqual(["3909406921"]);
|
|
852
869
|
expect(order?.exchange_price_difference).toBe(3.45);
|
|
853
870
|
});
|
|
854
871
|
|
|
872
|
+
test("the ANSWER pairs by position even where the stored fields sort separately", async () => {
|
|
873
|
+
// `#W5866402` is the case that makes the difference visible: the two
|
|
874
|
+
// requested replacements sort into the OTHER order, so the two stored lists
|
|
875
|
+
// — tau2's `sorted(item_ids)` / `sorted(new_item_ids)`, kept for fidelity
|
|
876
|
+
// with its expected end states — read as espresso→sneaker if anything
|
|
877
|
+
// treats them as a pairing. `planItemSwap` priced this positionally, so the
|
|
878
|
+
// result has to report the pairing that was priced, not the one the sort
|
|
879
|
+
// produced.
|
|
880
|
+
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
881
|
+
const result = ok<ExchangeResult>(
|
|
882
|
+
await exchangeDeliveredOrderItems.execute(
|
|
883
|
+
{
|
|
884
|
+
order_id: "#W5866402",
|
|
885
|
+
item_ids: ["6242772310", "9727387530"],
|
|
886
|
+
new_item_ids: ["7407838442", "2509076505"],
|
|
887
|
+
payment_method_id: "paypal_8049766",
|
|
888
|
+
},
|
|
889
|
+
ctx,
|
|
890
|
+
),
|
|
891
|
+
);
|
|
892
|
+
|
|
893
|
+
expect(result.exchanges).toEqual([
|
|
894
|
+
{ item_id: "6242772310", new_item_id: "7407838442", price_difference: 85.88 },
|
|
895
|
+
{ item_id: "9727387530", new_item_id: "2509076505", price_difference: -18.25 },
|
|
896
|
+
]);
|
|
897
|
+
// The per-line differences add up to the quoted total — which is what makes
|
|
898
|
+
// the answer internally consistent rather than two numbers side by side.
|
|
899
|
+
expect(result.exchanges.reduce((sum, one) => sum + one.price_difference, 0)).toBeCloseTo(
|
|
900
|
+
result.price_difference,
|
|
901
|
+
5,
|
|
902
|
+
);
|
|
903
|
+
|
|
904
|
+
// The stored fields keep tau2's shape: two sorted SETS, and the second one
|
|
905
|
+
// really is in the other order.
|
|
906
|
+
const order = retailSlot.get(ctx).store.orders["#W5866402"];
|
|
907
|
+
expect(order?.exchange_items).toEqual(["6242772310", "9727387530"]);
|
|
908
|
+
expect(order?.exchange_new_items).toEqual(["2509076505", "7407838442"]);
|
|
909
|
+
});
|
|
910
|
+
|
|
855
911
|
test("does NOT move the gift-card balance — it is a request, not a settlement", async () => {
|
|
856
912
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
857
913
|
await exchangeDeliveredOrderItems.execute(
|
|
@@ -863,7 +919,8 @@ describe("exchange_delivered_order_items", () => {
|
|
|
863
919
|
},
|
|
864
920
|
ctx,
|
|
865
921
|
);
|
|
866
|
-
const card =
|
|
922
|
+
const card =
|
|
923
|
+
retailSlot.get(ctx).store.users.aarav_anderson_8794?.payment_methods.gift_card_7245904;
|
|
867
924
|
expect(card?.source === "gift_card" && card.balance).toBe(17);
|
|
868
925
|
});
|
|
869
926
|
|
|
@@ -878,7 +935,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
878
935
|
},
|
|
879
936
|
ctx,
|
|
880
937
|
);
|
|
881
|
-
expect(
|
|
938
|
+
expect(retailSlot.get(ctx).store.orders["#W4316152"]?.items.map((i) => i.item_id)).toEqual([
|
|
882
939
|
"7292993796",
|
|
883
940
|
"7292993796",
|
|
884
941
|
]);
|
|
@@ -886,30 +943,34 @@ describe("exchange_delivered_order_items", () => {
|
|
|
886
943
|
|
|
887
944
|
test("exchanges both copies of a duplicate and doubles the difference", async () => {
|
|
888
945
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
889
|
-
const result = (
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
946
|
+
const result = ok<ExchangeResult>(
|
|
947
|
+
await exchangeDeliveredOrderItems.execute(
|
|
948
|
+
{
|
|
949
|
+
order_id: "#W4316152",
|
|
950
|
+
item_ids: ["7292993796", "7292993796"],
|
|
951
|
+
new_item_ids: ["3909406921", "3909406921"],
|
|
952
|
+
payment_method_id: "gift_card_7245904",
|
|
953
|
+
},
|
|
954
|
+
ctx,
|
|
955
|
+
),
|
|
956
|
+
);
|
|
957
|
+
expect(result.price_difference).toBe(6.9);
|
|
899
958
|
});
|
|
900
959
|
|
|
901
960
|
test("records a negative difference as a refund direction", async () => {
|
|
902
961
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
903
|
-
const result = (
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
962
|
+
const result = ok<ExchangeResult>(
|
|
963
|
+
await exchangeDeliveredOrderItems.execute(
|
|
964
|
+
{
|
|
965
|
+
order_id: "#W5866402",
|
|
966
|
+
item_ids: ["6242772310"],
|
|
967
|
+
new_item_ids: ["6200867091"],
|
|
968
|
+
payment_method_id: "gift_card_7794233",
|
|
969
|
+
},
|
|
970
|
+
ctx,
|
|
971
|
+
),
|
|
972
|
+
);
|
|
973
|
+
expect(result.price_difference).toBe(-40.86);
|
|
913
974
|
});
|
|
914
975
|
|
|
915
976
|
test("refuses a gift card that cannot cover the difference", async () => {
|
|
@@ -923,8 +984,8 @@ describe("exchange_delivered_order_items", () => {
|
|
|
923
984
|
},
|
|
924
985
|
ctx,
|
|
925
986
|
);
|
|
926
|
-
expect(
|
|
927
|
-
expect(
|
|
987
|
+
expect(isToolFailure(result) && result.error.toLowerCase()).toContain("balance");
|
|
988
|
+
expect(retailSlot.get(ctx).store.orders["#W9311069"]?.status).toBe("delivered");
|
|
928
989
|
});
|
|
929
990
|
|
|
930
991
|
test("refuses a cross-product swap", async () => {
|
|
@@ -938,7 +999,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
938
999
|
},
|
|
939
1000
|
ctx,
|
|
940
1001
|
);
|
|
941
|
-
expect(
|
|
1002
|
+
expect(isToolFailure(result)).toBe(true);
|
|
942
1003
|
});
|
|
943
1004
|
|
|
944
1005
|
test("refuses an unavailable target", async () => {
|
|
@@ -952,7 +1013,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
952
1013
|
},
|
|
953
1014
|
ctx,
|
|
954
1015
|
);
|
|
955
|
-
expect(
|
|
1016
|
+
expect(isToolFailure(result) && result.error.toLowerCase()).toContain("not available");
|
|
956
1017
|
});
|
|
957
1018
|
|
|
958
1019
|
test("refuses a pending order and refuses a second exchange", async () => {
|
|
@@ -966,7 +1027,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
966
1027
|
},
|
|
967
1028
|
ctx,
|
|
968
1029
|
);
|
|
969
|
-
expect(
|
|
1030
|
+
expect(isToolFailure(pending)).toBe(true);
|
|
970
1031
|
|
|
971
1032
|
await exchangeDeliveredOrderItems.execute(
|
|
972
1033
|
{
|
|
@@ -986,7 +1047,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
986
1047
|
},
|
|
987
1048
|
ctx,
|
|
988
1049
|
);
|
|
989
|
-
expect(
|
|
1050
|
+
expect(isToolFailure(again)).toBe(true);
|
|
990
1051
|
});
|
|
991
1052
|
|
|
992
1053
|
test("a returned order can no longer be exchanged", async () => {
|
|
@@ -1008,7 +1069,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
1008
1069
|
},
|
|
1009
1070
|
ctx,
|
|
1010
1071
|
);
|
|
1011
|
-
expect(
|
|
1072
|
+
expect(isToolFailure(result)).toBe(true);
|
|
1012
1073
|
});
|
|
1013
1074
|
});
|
|
1014
1075
|
|
|
@@ -1020,11 +1081,13 @@ interface TransferResult {
|
|
|
1020
1081
|
|
|
1021
1082
|
describe("transfer_to_human_agents", () => {
|
|
1022
1083
|
test("works without authentication — the escape hatch cannot be gated", async () => {
|
|
1023
|
-
const result = (
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1084
|
+
const result = ok<TransferResult>(
|
|
1085
|
+
await transferToHumanAgents.execute(
|
|
1086
|
+
{ summary: "Caller wants to dispute a charge from 2019." },
|
|
1087
|
+
makeCtx(),
|
|
1088
|
+
),
|
|
1089
|
+
);
|
|
1090
|
+
expect(isToolFailure(result)).toBe(false);
|
|
1091
|
+
expect(result.transferred).toBe(true);
|
|
1029
1092
|
});
|
|
1030
1093
|
});
|