@ucptools/validator 1.0.0 → 1.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.
Files changed (330) hide show
  1. package/dist/auth/config.d.ts +20 -0
  2. package/dist/auth/config.d.ts.map +1 -0
  3. package/dist/auth/config.js +114 -0
  4. package/dist/auth/config.js.map +1 -0
  5. package/dist/auth/index.d.ts +5 -0
  6. package/dist/auth/index.d.ts.map +1 -0
  7. package/dist/auth/index.js +17 -0
  8. package/dist/auth/index.js.map +1 -0
  9. package/dist/auth/middleware.d.ts +45 -0
  10. package/dist/auth/middleware.d.ts.map +1 -0
  11. package/dist/auth/middleware.js +170 -0
  12. package/dist/auth/middleware.js.map +1 -0
  13. package/dist/auth/service.d.ts +80 -0
  14. package/dist/auth/service.d.ts.map +1 -0
  15. package/dist/auth/service.js +298 -0
  16. package/dist/auth/service.js.map +1 -0
  17. package/dist/cli/index.d.ts +6 -0
  18. package/dist/cli/index.d.ts.map +1 -0
  19. package/dist/cli/index.js +375 -0
  20. package/dist/cli/index.js.map +1 -0
  21. package/dist/cli/mock-server.d.ts +20 -0
  22. package/dist/cli/mock-server.d.ts.map +1 -0
  23. package/dist/cli/mock-server.js +261 -0
  24. package/dist/cli/mock-server.js.map +1 -0
  25. package/dist/compliance/compliance-generator.d.ts +34 -0
  26. package/dist/compliance/compliance-generator.d.ts.map +1 -0
  27. package/dist/compliance/compliance-generator.js +320 -0
  28. package/dist/compliance/compliance-generator.js.map +1 -0
  29. package/dist/compliance/index.d.ts +8 -0
  30. package/dist/compliance/index.d.ts.map +1 -0
  31. package/dist/compliance/index.js +17 -0
  32. package/dist/compliance/index.js.map +1 -0
  33. package/dist/compliance/templates.d.ts +34 -0
  34. package/dist/compliance/templates.d.ts.map +1 -0
  35. package/{src/compliance/templates.ts → dist/compliance/templates.js} +117 -155
  36. package/dist/compliance/templates.js.map +1 -0
  37. package/dist/compliance/types.d.ts +64 -0
  38. package/dist/compliance/types.d.ts.map +1 -0
  39. package/dist/compliance/types.js +64 -0
  40. package/dist/compliance/types.js.map +1 -0
  41. package/dist/db/index.d.ts +17 -0
  42. package/dist/db/index.d.ts.map +1 -0
  43. package/dist/db/index.js +80 -0
  44. package/dist/db/index.js.map +1 -0
  45. package/dist/db/schema.d.ts +3886 -0
  46. package/dist/db/schema.d.ts.map +1 -0
  47. package/dist/db/schema.js +425 -0
  48. package/dist/db/schema.js.map +1 -0
  49. package/dist/db/utils.d.ts +252 -0
  50. package/dist/db/utils.d.ts.map +1 -0
  51. package/dist/db/utils.js +295 -0
  52. package/dist/db/utils.js.map +1 -0
  53. package/dist/feed-analyzer/feed-analyzer.d.ts +26 -0
  54. package/dist/feed-analyzer/feed-analyzer.d.ts.map +1 -0
  55. package/{src/feed-analyzer/feed-analyzer.ts → dist/feed-analyzer/feed-analyzer.js} +856 -726
  56. package/dist/feed-analyzer/feed-analyzer.js.map +1 -0
  57. package/dist/feed-analyzer/index.d.ts +8 -0
  58. package/dist/feed-analyzer/index.d.ts.map +1 -0
  59. package/dist/feed-analyzer/index.js +19 -0
  60. package/dist/feed-analyzer/index.js.map +1 -0
  61. package/dist/feed-analyzer/types.d.ts +285 -0
  62. package/dist/feed-analyzer/types.d.ts.map +1 -0
  63. package/dist/feed-analyzer/types.js +175 -0
  64. package/dist/feed-analyzer/types.js.map +1 -0
  65. package/{src/generator/index.ts → dist/generator/index.d.ts} +1 -1
  66. package/dist/generator/index.d.ts.map +1 -0
  67. package/dist/generator/index.js +13 -0
  68. package/dist/generator/index.js.map +1 -0
  69. package/dist/generator/key-generator.d.ts +24 -0
  70. package/dist/generator/key-generator.d.ts.map +1 -0
  71. package/dist/generator/key-generator.js +144 -0
  72. package/dist/generator/key-generator.js.map +1 -0
  73. package/dist/generator/profile-builder.d.ts +15 -0
  74. package/dist/generator/profile-builder.d.ts.map +1 -0
  75. package/dist/generator/profile-builder.js +338 -0
  76. package/dist/generator/profile-builder.js.map +1 -0
  77. package/dist/hosting/artifacts-generator.d.ts +10 -0
  78. package/dist/hosting/artifacts-generator.d.ts.map +1 -0
  79. package/{src/hosting/artifacts-generator.ts → dist/hosting/artifacts-generator.js} +191 -241
  80. package/dist/hosting/artifacts-generator.js.map +1 -0
  81. package/{src/hosting/index.ts → dist/hosting/index.d.ts} +1 -1
  82. package/dist/hosting/index.d.ts.map +1 -0
  83. package/dist/hosting/index.js +10 -0
  84. package/dist/hosting/index.js.map +1 -0
  85. package/dist/index.d.ts +18 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +78 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/lib/analytics.d.ts +337 -0
  90. package/dist/lib/analytics.d.ts.map +1 -0
  91. package/dist/lib/analytics.js +188 -0
  92. package/dist/lib/analytics.js.map +1 -0
  93. package/{src/security/index.ts → dist/security/index.d.ts} +8 -15
  94. package/dist/security/index.d.ts.map +1 -0
  95. package/dist/security/index.js +12 -0
  96. package/dist/security/index.js.map +1 -0
  97. package/dist/security/security-scanner.d.ts +10 -0
  98. package/dist/security/security-scanner.d.ts.map +1 -0
  99. package/dist/security/security-scanner.js +669 -0
  100. package/dist/security/security-scanner.js.map +1 -0
  101. package/dist/security/types.d.ts +80 -0
  102. package/dist/security/types.d.ts.map +1 -0
  103. package/dist/security/types.js +21 -0
  104. package/dist/security/types.js.map +1 -0
  105. package/dist/services/analytics.d.ts +114 -0
  106. package/dist/services/analytics.d.ts.map +1 -0
  107. package/dist/services/analytics.js +862 -0
  108. package/dist/services/analytics.js.map +1 -0
  109. package/dist/services/badge.d.ts +31 -0
  110. package/dist/services/badge.d.ts.map +1 -0
  111. package/dist/services/badge.js +152 -0
  112. package/dist/services/badge.js.map +1 -0
  113. package/dist/services/cron.d.ts +125 -0
  114. package/dist/services/cron.d.ts.map +1 -0
  115. package/dist/services/cron.js +613 -0
  116. package/dist/services/cron.js.map +1 -0
  117. package/dist/services/directory.d.ts +106 -0
  118. package/dist/services/directory.d.ts.map +1 -0
  119. package/dist/services/directory.js +351 -0
  120. package/dist/services/directory.js.map +1 -0
  121. package/dist/services/email.d.ts +112 -0
  122. package/dist/services/email.d.ts.map +1 -0
  123. package/dist/services/email.js +772 -0
  124. package/dist/services/email.js.map +1 -0
  125. package/dist/services/hosted-profiles.d.ts +77 -0
  126. package/dist/services/hosted-profiles.d.ts.map +1 -0
  127. package/dist/services/hosted-profiles.js +433 -0
  128. package/dist/services/hosted-profiles.js.map +1 -0
  129. package/dist/services/latency.d.ts +67 -0
  130. package/dist/services/latency.d.ts.map +1 -0
  131. package/dist/services/latency.js +274 -0
  132. package/dist/services/latency.js.map +1 -0
  133. package/dist/services/manifest-compliance.d.ts +64 -0
  134. package/dist/services/manifest-compliance.d.ts.map +1 -0
  135. package/dist/services/manifest-compliance.js +271 -0
  136. package/dist/services/manifest-compliance.js.map +1 -0
  137. package/dist/services/monitoring-diff.d.ts +31 -0
  138. package/dist/services/monitoring-diff.d.ts.map +1 -0
  139. package/dist/services/monitoring-diff.js +189 -0
  140. package/dist/services/monitoring-diff.js.map +1 -0
  141. package/dist/services/notifications.d.ts +46 -0
  142. package/dist/services/notifications.d.ts.map +1 -0
  143. package/dist/services/notifications.js +88 -0
  144. package/dist/services/notifications.js.map +1 -0
  145. package/dist/services/stripe.d.ts +93 -0
  146. package/dist/services/stripe.d.ts.map +1 -0
  147. package/dist/services/stripe.js +490 -0
  148. package/dist/services/stripe.js.map +1 -0
  149. package/dist/services/validation-history.d.ts +99 -0
  150. package/dist/services/validation-history.d.ts.map +1 -0
  151. package/dist/services/validation-history.js +344 -0
  152. package/dist/services/validation-history.js.map +1 -0
  153. package/dist/services/validation-logging.d.ts +103 -0
  154. package/dist/services/validation-logging.d.ts.map +1 -0
  155. package/dist/services/validation-logging.js +210 -0
  156. package/dist/services/validation-logging.js.map +1 -0
  157. package/dist/services/validation.d.ts +119 -0
  158. package/dist/services/validation.d.ts.map +1 -0
  159. package/dist/services/validation.js +1185 -0
  160. package/dist/services/validation.js.map +1 -0
  161. package/dist/simulator/agent-simulator.d.ts +69 -0
  162. package/dist/simulator/agent-simulator.d.ts.map +1 -0
  163. package/dist/simulator/agent-simulator.js +870 -0
  164. package/dist/simulator/agent-simulator.js.map +1 -0
  165. package/{src/simulator/index.ts → dist/simulator/index.d.ts} +7 -7
  166. package/dist/simulator/index.d.ts.map +1 -0
  167. package/dist/simulator/index.js +23 -0
  168. package/dist/simulator/index.js.map +1 -0
  169. package/{src/simulator/types.ts → dist/simulator/types.d.ts} +171 -170
  170. package/dist/simulator/types.d.ts.map +1 -0
  171. package/dist/simulator/types.js +18 -0
  172. package/dist/simulator/types.js.map +1 -0
  173. package/dist/types/acp-validation.d.ts +87 -0
  174. package/dist/types/acp-validation.d.ts.map +1 -0
  175. package/dist/types/acp-validation.js +40 -0
  176. package/dist/types/acp-validation.js.map +1 -0
  177. package/dist/types/analytics.d.ts +182 -0
  178. package/dist/types/analytics.d.ts.map +1 -0
  179. package/dist/types/analytics.js +7 -0
  180. package/dist/types/analytics.js.map +1 -0
  181. package/dist/types/generator.d.ts +106 -0
  182. package/dist/types/generator.d.ts.map +1 -0
  183. package/dist/types/generator.js +6 -0
  184. package/dist/types/generator.js.map +1 -0
  185. package/{src/types/index.ts → dist/types/index.d.ts} +1 -1
  186. package/dist/types/index.d.ts.map +1 -0
  187. package/dist/types/index.js +23 -0
  188. package/dist/types/index.js.map +1 -0
  189. package/dist/types/ucp-profile.d.ts +111 -0
  190. package/dist/types/ucp-profile.d.ts.map +1 -0
  191. package/dist/types/ucp-profile.js +45 -0
  192. package/dist/types/ucp-profile.js.map +1 -0
  193. package/dist/types/validation.d.ts +76 -0
  194. package/dist/types/validation.d.ts.map +1 -0
  195. package/dist/types/validation.js +42 -0
  196. package/dist/types/validation.js.map +1 -0
  197. package/dist/validator/acp/index.d.ts +31 -0
  198. package/dist/validator/acp/index.d.ts.map +1 -0
  199. package/dist/validator/acp/index.js +574 -0
  200. package/dist/validator/acp/index.js.map +1 -0
  201. package/dist/validator/index.d.ts +26 -0
  202. package/dist/validator/index.d.ts.map +1 -0
  203. package/dist/validator/index.js +161 -0
  204. package/dist/validator/index.js.map +1 -0
  205. package/dist/validator/network-validator.d.ts +28 -0
  206. package/dist/validator/network-validator.d.ts.map +1 -0
  207. package/dist/validator/network-validator.js +319 -0
  208. package/dist/validator/network-validator.js.map +1 -0
  209. package/dist/validator/rules-validator.d.ts +19 -0
  210. package/dist/validator/rules-validator.d.ts.map +1 -0
  211. package/dist/validator/rules-validator.js +306 -0
  212. package/dist/validator/rules-validator.js.map +1 -0
  213. package/dist/validator/sdk-validator.d.ts +58 -0
  214. package/dist/validator/sdk-validator.d.ts.map +1 -0
  215. package/{src/validator/sdk-validator.ts → dist/validator/sdk-validator.js} +273 -330
  216. package/dist/validator/sdk-validator.js.map +1 -0
  217. package/dist/validator/structural-validator.d.ts +11 -0
  218. package/dist/validator/structural-validator.d.ts.map +1 -0
  219. package/dist/validator/structural-validator.js +549 -0
  220. package/dist/validator/structural-validator.js.map +1 -0
  221. package/dist/validator/utils.d.ts +51 -0
  222. package/dist/validator/utils.d.ts.map +1 -0
  223. package/dist/validator/utils.js +132 -0
  224. package/dist/validator/utils.js.map +1 -0
  225. package/package.json +44 -12
  226. package/CLAUDE.md +0 -109
  227. package/api/analyze-feed.js +0 -140
  228. package/api/badge.js +0 -185
  229. package/api/benchmark.js +0 -177
  230. package/api/directory-stats.ts +0 -29
  231. package/api/directory.ts +0 -73
  232. package/api/generate-compliance.js +0 -143
  233. package/api/generate-schema.js +0 -457
  234. package/api/generate.js +0 -132
  235. package/api/security-scan.js +0 -133
  236. package/api/simulate.js +0 -187
  237. package/api/tsconfig.json +0 -10
  238. package/api/validate.js +0 -1351
  239. package/apify-actor/.actor/actor.json +0 -68
  240. package/apify-actor/.actor/input_schema.json +0 -32
  241. package/apify-actor/APIFY-STORE-LISTING.md +0 -412
  242. package/apify-actor/Dockerfile +0 -8
  243. package/apify-actor/README.md +0 -166
  244. package/apify-actor/main.ts +0 -111
  245. package/apify-actor/package.json +0 -17
  246. package/apify-actor/src/main.js +0 -199
  247. package/docs/BRAND-IDENTITY.md +0 -238
  248. package/docs/BRAND-STYLE-GUIDE.md +0 -356
  249. package/drizzle/0000_black_king_cobra.sql +0 -39
  250. package/drizzle/meta/0000_snapshot.json +0 -309
  251. package/drizzle/meta/_journal.json +0 -13
  252. package/drizzle.config.ts +0 -10
  253. package/public/.well-known/ucp +0 -25
  254. package/public/android-chrome-192x192.png +0 -0
  255. package/public/android-chrome-512x512.png +0 -0
  256. package/public/apple-touch-icon.png +0 -0
  257. package/public/brand.css +0 -321
  258. package/public/directory.html +0 -701
  259. package/public/favicon-16x16.png +0 -0
  260. package/public/favicon-32x32.png +0 -0
  261. package/public/favicon.ico +0 -0
  262. package/public/guides/bigcommerce.html +0 -743
  263. package/public/guides/fastucp.html +0 -838
  264. package/public/guides/magento.html +0 -779
  265. package/public/guides/shopify.html +0 -726
  266. package/public/guides/squarespace.html +0 -749
  267. package/public/guides/wix.html +0 -747
  268. package/public/guides/woocommerce.html +0 -733
  269. package/public/index.html +0 -3835
  270. package/public/learn.html +0 -396
  271. package/public/logo.jpeg +0 -0
  272. package/public/og-image-icon.png +0 -0
  273. package/public/og-image.png +0 -0
  274. package/public/robots.txt +0 -6
  275. package/public/site.webmanifest +0 -31
  276. package/public/sitemap.xml +0 -69
  277. package/public/social/linkedin-banner-1128x191.png +0 -0
  278. package/public/social/temp.PNG +0 -0
  279. package/public/social/x-header-1500x500.png +0 -0
  280. package/public/verify.html +0 -410
  281. package/scripts/generate-favicons.js +0 -44
  282. package/scripts/generate-ico.js +0 -23
  283. package/scripts/generate-og-image.js +0 -45
  284. package/scripts/reset-db.ts +0 -77
  285. package/scripts/seed-db.ts +0 -71
  286. package/scripts/setup-benchmark-db.js +0 -70
  287. package/src/api/server.ts +0 -266
  288. package/src/cli/index.ts +0 -302
  289. package/src/compliance/compliance-generator.ts +0 -452
  290. package/src/compliance/index.ts +0 -28
  291. package/src/compliance/types.ts +0 -170
  292. package/src/db/index.ts +0 -28
  293. package/src/db/schema.ts +0 -84
  294. package/src/feed-analyzer/index.ts +0 -34
  295. package/src/feed-analyzer/types.ts +0 -354
  296. package/src/generator/key-generator.ts +0 -124
  297. package/src/generator/profile-builder.ts +0 -402
  298. package/src/index.ts +0 -105
  299. package/src/security/security-scanner.ts +0 -604
  300. package/src/security/types.ts +0 -55
  301. package/src/services/directory.ts +0 -434
  302. package/src/simulator/agent-simulator.ts +0 -941
  303. package/src/types/generator.ts +0 -140
  304. package/src/types/ucp-profile.ts +0 -140
  305. package/src/types/validation.ts +0 -89
  306. package/src/validator/index.ts +0 -194
  307. package/src/validator/network-validator.ts +0 -417
  308. package/src/validator/rules-validator.ts +0 -297
  309. package/src/validator/structural-validator.ts +0 -476
  310. package/tests/fixtures/non-compliant-profile.json +0 -25
  311. package/tests/fixtures/official-sample-profile.json +0 -75
  312. package/tests/integration/benchmark.test.ts +0 -207
  313. package/tests/integration/database.test.ts +0 -163
  314. package/tests/integration/directory-api.test.ts +0 -268
  315. package/tests/integration/simulate-api.test.ts +0 -230
  316. package/tests/integration/validate-api.test.ts +0 -269
  317. package/tests/setup.ts +0 -15
  318. package/tests/unit/agent-simulator.test.ts +0 -575
  319. package/tests/unit/compliance-generator.test.ts +0 -374
  320. package/tests/unit/directory-service.test.ts +0 -272
  321. package/tests/unit/feed-analyzer.test.ts +0 -517
  322. package/tests/unit/lint-suggestions.test.ts +0 -423
  323. package/tests/unit/official-samples.test.ts +0 -211
  324. package/tests/unit/pdf-report.test.ts +0 -390
  325. package/tests/unit/sdk-validator.test.ts +0 -531
  326. package/tests/unit/security-scanner.test.ts +0 -410
  327. package/tests/unit/validation.test.ts +0 -390
  328. package/tsconfig.json +0 -20
  329. package/vercel.json +0 -34
  330. package/vitest.config.ts +0 -22
@@ -1,575 +0,0 @@
1
- /**
2
- * Tests for AI Agent Simulator
3
- */
4
-
5
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
6
- import {
7
- simulateDiscoveryFlow,
8
- inspectCapabilities,
9
- inspectServices,
10
- simulateRestApi,
11
- simulateCheckoutFlow,
12
- simulatePaymentReadiness,
13
- simulateAgentInteraction,
14
- calculateScore,
15
- generateRecommendations,
16
- } from '../../src/simulator/agent-simulator.js';
17
- import type { UcpProfile } from '../../src/types/ucp-profile.js';
18
- import type { DiscoveryFlowResult, CapabilityInspectionResult, ServiceInspectionResult } from '../../src/simulator/types.js';
19
-
20
- // Mock fetch globally
21
- const mockFetch = vi.fn();
22
- global.fetch = mockFetch;
23
-
24
- // Sample UCP profile for testing
25
- const sampleProfile: UcpProfile = {
26
- ucp: {
27
- version: '2026-01-11',
28
- services: {
29
- 'dev.ucp.shopping': {
30
- version: '2026-01-11',
31
- spec: 'https://ucp.dev/specs/shopping',
32
- rest: {
33
- schema: 'https://ucp.dev/services/shopping/openapi.json',
34
- endpoint: 'https://example.com/api',
35
- },
36
- },
37
- },
38
- capabilities: [
39
- {
40
- name: 'dev.ucp.shopping.checkout',
41
- version: '2026-01-11',
42
- spec: 'https://ucp.dev/specs/shopping/checkout',
43
- schema: 'https://ucp.dev/schemas/shopping/checkout.json',
44
- },
45
- {
46
- name: 'dev.ucp.shopping.order',
47
- version: '2026-01-11',
48
- spec: 'https://ucp.dev/specs/shopping/order',
49
- schema: 'https://ucp.dev/schemas/shopping/order.json',
50
- extends: 'dev.ucp.shopping.checkout',
51
- },
52
- ],
53
- },
54
- signing_keys: [
55
- {
56
- kty: 'EC',
57
- crv: 'P-256',
58
- x: 'test-x',
59
- y: 'test-y',
60
- kid: 'key-1',
61
- },
62
- ],
63
- payment: {
64
- handlers: [
65
- {
66
- id: 'stripe',
67
- name: 'com.stripe.checkout',
68
- version: '2026-01-11',
69
- spec: 'https://stripe.com/docs/ucp',
70
- },
71
- ],
72
- },
73
- };
74
-
75
- // Minimal profile without optional features
76
- const minimalProfile: UcpProfile = {
77
- ucp: {
78
- version: '2026-01-11',
79
- services: {
80
- 'dev.ucp.shopping': {
81
- version: '2026-01-11',
82
- spec: 'https://ucp.dev/specs/shopping',
83
- rest: {
84
- schema: 'https://example.com/api/schema.json',
85
- endpoint: 'https://example.com/api',
86
- },
87
- },
88
- },
89
- capabilities: [],
90
- },
91
- };
92
-
93
- describe('Agent Simulator', () => {
94
- beforeEach(() => {
95
- mockFetch.mockReset();
96
- });
97
-
98
- afterEach(() => {
99
- vi.restoreAllMocks();
100
- });
101
-
102
- describe('simulateDiscoveryFlow', () => {
103
- it('should successfully discover a UCP profile', async () => {
104
- mockFetch.mockResolvedValueOnce({
105
- ok: true,
106
- status: 200,
107
- headers: new Map([['content-type', 'application/json']]),
108
- json: async () => sampleProfile,
109
- });
110
-
111
- const result = await simulateDiscoveryFlow('example.com', 5000);
112
-
113
- expect(result.success).toBe(true);
114
- expect(result.profileUrl).toBe('https://example.com/.well-known/ucp');
115
- expect(result.services).toContain('dev.ucp.shopping');
116
- expect(result.capabilities).toContain('dev.ucp.shopping.checkout');
117
- expect(result.transports).toContain('rest');
118
- });
119
-
120
- it('should try .well-known/ucp.json if .well-known/ucp fails', async () => {
121
- // First call fails
122
- mockFetch.mockResolvedValueOnce({
123
- ok: false,
124
- status: 404,
125
- });
126
- // Second call succeeds
127
- mockFetch.mockResolvedValueOnce({
128
- ok: true,
129
- status: 200,
130
- headers: new Map([['content-type', 'application/json']]),
131
- json: async () => sampleProfile,
132
- });
133
-
134
- const result = await simulateDiscoveryFlow('example.com', 5000);
135
-
136
- expect(result.success).toBe(true);
137
- expect(result.profileUrl).toBe('https://example.com/.well-known/ucp.json');
138
- });
139
-
140
- it('should fail if no UCP profile found', async () => {
141
- mockFetch.mockResolvedValue({
142
- ok: false,
143
- status: 404,
144
- });
145
-
146
- const result = await simulateDiscoveryFlow('example.com', 5000);
147
-
148
- expect(result.success).toBe(false);
149
- expect(result.steps.some(s => s.status === 'failed')).toBe(true);
150
- });
151
-
152
- it('should detect available transports', async () => {
153
- const profileWithMultipleTransports = {
154
- ...sampleProfile,
155
- ucp: {
156
- ...sampleProfile.ucp,
157
- services: {
158
- 'dev.ucp.shopping': {
159
- version: '2026-01-11',
160
- spec: 'https://ucp.dev/specs/shopping',
161
- rest: {
162
- schema: 'https://example.com/openapi.json',
163
- endpoint: 'https://example.com/api',
164
- },
165
- mcp: {
166
- schema: 'https://example.com/mcp.json',
167
- endpoint: 'https://example.com/mcp',
168
- },
169
- },
170
- },
171
- },
172
- };
173
-
174
- mockFetch.mockResolvedValueOnce({
175
- ok: true,
176
- status: 200,
177
- headers: new Map([['content-type', 'application/json']]),
178
- json: async () => profileWithMultipleTransports,
179
- });
180
-
181
- const result = await simulateDiscoveryFlow('example.com', 5000);
182
-
183
- expect(result.transports).toContain('rest');
184
- expect(result.transports).toContain('mcp');
185
- });
186
-
187
- it('should handle network timeout gracefully', async () => {
188
- mockFetch.mockImplementation(() => {
189
- return new Promise((_, reject) => {
190
- setTimeout(() => reject(new Error('timeout')), 100);
191
- });
192
- });
193
-
194
- const result = await simulateDiscoveryFlow('example.com', 50);
195
-
196
- expect(result.success).toBe(false);
197
- });
198
- });
199
-
200
- describe('inspectCapabilities', () => {
201
- it('should inspect capabilities and check schema accessibility', async () => {
202
- // Schema fetch succeeds
203
- mockFetch.mockResolvedValueOnce({
204
- ok: true,
205
- status: 200,
206
- headers: new Map([['content-type', 'application/json']]),
207
- json: async () => ({ type: 'object' }),
208
- });
209
- // Spec fetch succeeds
210
- mockFetch.mockResolvedValueOnce({
211
- ok: true,
212
- status: 200,
213
- });
214
- // Second capability schema
215
- mockFetch.mockResolvedValueOnce({
216
- ok: true,
217
- status: 200,
218
- headers: new Map([['content-type', 'application/json']]),
219
- json: async () => ({ type: 'object' }),
220
- });
221
- // Second capability spec
222
- mockFetch.mockResolvedValueOnce({
223
- ok: true,
224
- status: 200,
225
- });
226
-
227
- const result = await inspectCapabilities(sampleProfile, 5000);
228
-
229
- expect(result.length).toBe(2);
230
- expect(result[0].name).toBe('dev.ucp.shopping.checkout');
231
- expect(result[0].schemaAccessible).toBe(true);
232
- expect(result[1].isExtension).toBe(true);
233
- expect(result[1].parentCapability).toBe('dev.ucp.shopping.checkout');
234
- });
235
-
236
- it('should handle inaccessible schemas', async () => {
237
- mockFetch.mockResolvedValue({
238
- ok: false,
239
- status: 404,
240
- });
241
-
242
- const result = await inspectCapabilities(sampleProfile, 5000);
243
-
244
- expect(result[0].schemaAccessible).toBe(false);
245
- });
246
- });
247
-
248
- describe('inspectServices', () => {
249
- it('should inspect REST service transport', async () => {
250
- // Schema check
251
- mockFetch.mockResolvedValueOnce({
252
- ok: true,
253
- status: 200,
254
- headers: new Map([['content-type', 'application/json']]),
255
- json: async () => ({ openapi: '3.0.0' }),
256
- });
257
- // Endpoint check
258
- mockFetch.mockResolvedValueOnce({
259
- ok: true,
260
- status: 200,
261
- });
262
-
263
- const result = await inspectServices(sampleProfile, 5000);
264
-
265
- expect(result.length).toBe(1);
266
- expect(result[0].name).toBe('dev.ucp.shopping');
267
- expect(result[0].transports.rest?.schemaAccessible).toBe(true);
268
- expect(result[0].transports.rest?.endpointResponsive).toBe(true);
269
- });
270
-
271
- it('should detect unresponsive endpoints', async () => {
272
- mockFetch.mockResolvedValueOnce({
273
- ok: true,
274
- status: 200,
275
- headers: new Map([['content-type', 'application/json']]),
276
- json: async () => ({}),
277
- });
278
- mockFetch.mockResolvedValueOnce({
279
- ok: false,
280
- status: 500,
281
- });
282
-
283
- const result = await inspectServices(sampleProfile, 5000);
284
-
285
- expect(result[0].transports.rest?.endpointResponsive).toBe(false);
286
- });
287
- });
288
-
289
- describe('simulateRestApi', () => {
290
- it('should load and analyze OpenAPI schema', async () => {
291
- const openApiSchema = {
292
- openapi: '3.0.0',
293
- paths: {
294
- '/checkout': { post: {} },
295
- '/orders': { get: {} },
296
- },
297
- };
298
-
299
- // Schema fetch
300
- mockFetch.mockResolvedValueOnce({
301
- ok: true,
302
- status: 200,
303
- headers: new Map([['content-type', 'application/json']]),
304
- json: async () => openApiSchema,
305
- });
306
- // Endpoint check
307
- mockFetch.mockResolvedValueOnce({
308
- ok: true,
309
- status: 200,
310
- });
311
-
312
- const result = await simulateRestApi(sampleProfile, 5000);
313
-
314
- expect(result.success).toBe(true);
315
- expect(result.schemaLoaded).toBe(true);
316
- expect(result.endpointAccessible).toBe(true);
317
- expect(result.steps.some(s => s.message.includes('2 operation path'))).toBe(true);
318
- });
319
-
320
- it('should skip if no REST service configured', async () => {
321
- const noRestProfile: UcpProfile = {
322
- ucp: {
323
- version: '2026-01-11',
324
- services: {
325
- 'dev.ucp.shopping': {
326
- version: '2026-01-11',
327
- spec: 'https://ucp.dev/specs/shopping',
328
- mcp: {
329
- schema: 'https://example.com/mcp.json',
330
- endpoint: 'https://example.com/mcp',
331
- },
332
- },
333
- },
334
- capabilities: [],
335
- },
336
- };
337
-
338
- const result = await simulateRestApi(noRestProfile, 5000);
339
-
340
- expect(result.success).toBe(false);
341
- expect(result.steps[0].status).toBe('skipped');
342
- });
343
- });
344
-
345
- describe('simulateCheckoutFlow', () => {
346
- it('should detect checkout capability', async () => {
347
- mockFetch.mockResolvedValueOnce({
348
- ok: true,
349
- status: 200,
350
- headers: new Map([['content-type', 'application/json']]),
351
- json: async () => ({ properties: { checkout_id: {} } }),
352
- });
353
-
354
- const result = await simulateCheckoutFlow(sampleProfile, 5000);
355
-
356
- expect(result.canCreateCheckout).toBe(true);
357
- expect(result.checkoutSchemaValid).toBe(true);
358
- });
359
-
360
- it('should detect order and fulfillment capabilities', async () => {
361
- const fullProfile: UcpProfile = {
362
- ucp: {
363
- version: '2026-01-11',
364
- services: {},
365
- capabilities: [
366
- { name: 'dev.ucp.shopping.checkout', version: '2026-01-11', spec: '', schema: '' },
367
- { name: 'dev.ucp.shopping.order', version: '2026-01-11', spec: '', schema: '' },
368
- { name: 'dev.ucp.shopping.fulfillment', version: '2026-01-11', spec: '', schema: '' },
369
- ],
370
- },
371
- };
372
-
373
- // Skip schema checks
374
- mockFetch.mockResolvedValue({ ok: false });
375
-
376
- const result = await simulateCheckoutFlow(fullProfile, 5000);
377
-
378
- expect(result.canCreateCheckout).toBe(true);
379
- expect(result.orderFlowSupported).toBe(true);
380
- expect(result.fulfillmentSupported).toBe(true);
381
- });
382
-
383
- it('should fail if no checkout capability', async () => {
384
- const result = await simulateCheckoutFlow(minimalProfile, 5000);
385
-
386
- expect(result.canCreateCheckout).toBe(false);
387
- expect(result.steps.some(s => s.status === 'failed')).toBe(true);
388
- });
389
- });
390
-
391
- describe('simulatePaymentReadiness', () => {
392
- it('should detect payment handlers', async () => {
393
- const result = await simulatePaymentReadiness(sampleProfile, 5000);
394
-
395
- expect(result.handlersFound).toBe(1);
396
- expect(result.steps.some(s => s.message.includes('payment handler'))).toBe(true);
397
- });
398
-
399
- it('should validate signing keys', async () => {
400
- const result = await simulatePaymentReadiness(sampleProfile, 5000);
401
-
402
- expect(result.webhookVerifiable).toBe(true);
403
- expect(result.signingKeyValid).toBe(true);
404
- });
405
-
406
- it('should detect missing payment config', async () => {
407
- const result = await simulatePaymentReadiness(minimalProfile, 5000);
408
-
409
- expect(result.handlersFound).toBe(0);
410
- expect(result.webhookVerifiable).toBe(false);
411
- });
412
- });
413
-
414
- describe('calculateScore', () => {
415
- it('should give high score for complete profile', () => {
416
- const discovery: DiscoveryFlowResult = {
417
- success: true,
418
- steps: [],
419
- capabilities: ['dev.ucp.shopping.checkout'],
420
- services: ['dev.ucp.shopping'],
421
- transports: ['rest'],
422
- };
423
-
424
- const capabilities: CapabilityInspectionResult[] = [
425
- { name: 'dev.ucp.shopping.checkout', version: '2026-01-11', schemaAccessible: true, specAccessible: true, isExtension: false },
426
- ];
427
-
428
- const services: ServiceInspectionResult[] = [
429
- { name: 'dev.ucp.shopping', version: '2026-01-11', transports: { rest: { endpoint: '', schemaAccessible: true, endpointResponsive: true } } },
430
- ];
431
-
432
- const checkout = { success: true, steps: [], canCreateCheckout: true, checkoutSchemaValid: true, orderFlowSupported: true, fulfillmentSupported: false };
433
- const payment = { success: true, steps: [], handlersFound: 1, webhookVerifiable: true, signingKeyValid: true };
434
-
435
- const score = calculateScore(discovery, capabilities, services, undefined, checkout, payment);
436
-
437
- expect(score).toBeGreaterThanOrEqual(70);
438
- });
439
-
440
- it('should give low score for failed discovery', () => {
441
- const discovery: DiscoveryFlowResult = {
442
- success: false,
443
- steps: [],
444
- capabilities: [],
445
- services: [],
446
- transports: [],
447
- };
448
-
449
- const score = calculateScore(discovery, [], [], undefined, undefined, undefined);
450
-
451
- expect(score).toBeLessThan(30);
452
- });
453
- });
454
-
455
- describe('generateRecommendations', () => {
456
- it('should recommend fixing discovery issues', () => {
457
- const discovery: DiscoveryFlowResult = {
458
- success: false,
459
- steps: [],
460
- capabilities: [],
461
- services: [],
462
- transports: [],
463
- };
464
-
465
- const recommendations = generateRecommendations(discovery, [], []);
466
-
467
- expect(recommendations.some(r => r.includes('/.well-known/ucp'))).toBe(true);
468
- });
469
-
470
- it('should recommend adding checkout capability', () => {
471
- const discovery: DiscoveryFlowResult = {
472
- success: true,
473
- steps: [],
474
- capabilities: [],
475
- services: ['dev.ucp.shopping'],
476
- transports: ['rest'],
477
- };
478
-
479
- const checkout = { success: false, steps: [], canCreateCheckout: false, checkoutSchemaValid: false, orderFlowSupported: false, fulfillmentSupported: false };
480
-
481
- const recommendations = generateRecommendations(discovery, [], [], undefined, checkout);
482
-
483
- expect(recommendations.some(r => r.includes('checkout capability'))).toBe(true);
484
- });
485
-
486
- it('should give positive message when all looks good', () => {
487
- const discovery: DiscoveryFlowResult = {
488
- success: true,
489
- steps: [],
490
- capabilities: ['dev.ucp.shopping.checkout'],
491
- services: ['dev.ucp.shopping'],
492
- transports: ['rest'],
493
- };
494
-
495
- const capabilities: CapabilityInspectionResult[] = [
496
- { name: 'dev.ucp.shopping.checkout', version: '2026-01-11', schemaAccessible: true, specAccessible: true, isExtension: false },
497
- ];
498
-
499
- const restApi = { success: true, steps: [], schemaLoaded: true, endpointAccessible: true, sampleOperations: [] };
500
- const checkout = { success: true, steps: [], canCreateCheckout: true, checkoutSchemaValid: true, orderFlowSupported: true, fulfillmentSupported: true };
501
- const payment = { success: true, steps: [], handlersFound: 1, webhookVerifiable: true, signingKeyValid: true };
502
-
503
- const recommendations = generateRecommendations(discovery, capabilities, [], restApi, checkout, payment);
504
-
505
- expect(recommendations.some(r => r.includes('well-configured'))).toBe(true);
506
- });
507
- });
508
-
509
- describe('simulateAgentInteraction (integration)', () => {
510
- it('should run full simulation successfully', async () => {
511
- // Profile fetch (discovery)
512
- mockFetch.mockResolvedValueOnce({
513
- ok: true,
514
- status: 200,
515
- headers: new Map([['content-type', 'application/json']]),
516
- json: async () => sampleProfile,
517
- });
518
- // Profile fetch again for detailed inspection
519
- mockFetch.mockResolvedValueOnce({
520
- ok: true,
521
- status: 200,
522
- headers: new Map([['content-type', 'application/json']]),
523
- json: async () => sampleProfile,
524
- });
525
- // All subsequent fetches succeed
526
- mockFetch.mockResolvedValue({
527
- ok: true,
528
- status: 200,
529
- headers: new Map([['content-type', 'application/json']]),
530
- json: async () => ({ type: 'object', properties: { checkout_id: {} } }),
531
- });
532
-
533
- const result = await simulateAgentInteraction('example.com', { timeoutMs: 10000 });
534
-
535
- expect(result.ok).toBe(true);
536
- expect(result.domain).toBe('example.com');
537
- expect(result.overallScore).toBeGreaterThan(0);
538
- expect(result.discovery.success).toBe(true);
539
- expect(result.summary.totalSteps).toBeGreaterThan(0);
540
- });
541
-
542
- it('should handle complete failure gracefully', async () => {
543
- mockFetch.mockResolvedValue({
544
- ok: false,
545
- status: 404,
546
- });
547
-
548
- const result = await simulateAgentInteraction('nonexistent.com', { timeoutMs: 5000 });
549
-
550
- expect(result.ok).toBe(false);
551
- expect(result.overallScore).toBe(0);
552
- expect(result.recommendations.length).toBeGreaterThan(0);
553
- });
554
-
555
- it('should include timing information', async () => {
556
- mockFetch.mockResolvedValueOnce({
557
- ok: true,
558
- status: 200,
559
- headers: new Map([['content-type', 'application/json']]),
560
- json: async () => sampleProfile,
561
- });
562
- mockFetch.mockResolvedValue({
563
- ok: true,
564
- status: 200,
565
- headers: new Map([['content-type', 'application/json']]),
566
- json: async () => ({}),
567
- });
568
-
569
- const result = await simulateAgentInteraction('example.com', { timeoutMs: 10000 });
570
-
571
- expect(result.durationMs).toBeGreaterThanOrEqual(0);
572
- expect(result.simulatedAt).toBeTruthy();
573
- });
574
- });
575
- });