@tscircuit/fake-snippets 0.0.109 → 0.0.111
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/.github/workflows/bun-formatcheck.yml +2 -2
- package/.github/workflows/bun-pver-release.yml +3 -3
- package/.github/workflows/bun-test.yml +1 -1
- package/.github/workflows/bun-typecheck.yml +2 -2
- package/.github/workflows/update-snapshots.yml +1 -1
- package/README.md +4 -0
- package/api/generated-index.js +37 -3
- package/biome.json +2 -1
- package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +32 -3
- package/bun-tests/fake-snippets-api/fixtures/preload.ts +18 -0
- package/bun-tests/fake-snippets-api/routes/orgs/add_member.test.ts +26 -0
- package/bun-tests/fake-snippets-api/routes/orgs/create.test.ts +37 -0
- package/bun-tests/fake-snippets-api/routes/orgs/get.test.ts +52 -0
- package/bun-tests/fake-snippets-api/routes/orgs/list.test.ts +17 -0
- package/bun-tests/fake-snippets-api/routes/orgs/list_members.test.ts +23 -0
- package/bun-tests/fake-snippets-api/routes/orgs/remove_member.test.ts +81 -0
- package/bun-tests/fake-snippets-api/routes/orgs/update.test.ts +151 -0
- package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +1 -1
- package/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +15 -13
- package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -24
- package/bun-tests/fake-snippets-api/routes/package_files/delete.test.ts +9 -9
- package/bun-tests/fake-snippets-api/routes/package_files/download.test.ts +4 -4
- package/bun-tests/fake-snippets-api/routes/package_files/get.test.ts +38 -28
- package/bun-tests/fake-snippets-api/routes/package_files/list.test.ts +23 -15
- package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +33 -0
- package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +4 -4
- package/bun-tests/fake-snippets-api/routes/package_releases/get_image_generation_fields.test.ts +38 -0
- package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +19 -0
- package/bun-tests/fake-snippets-api/routes/packages/fork.test.ts +3 -4
- package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +30 -0
- package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +4 -2
- package/bun-tests/fake-snippets-api/routes/packages/list-1.test.ts +34 -0
- package/bun.lock +361 -453
- package/bunfig.toml +2 -1
- package/dist/bundle.js +1313 -639
- package/dist/index.d.ts +313 -6
- package/dist/index.js +328 -24
- package/dist/schema.d.ts +290 -1
- package/dist/schema.js +54 -1
- package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
- package/fake-snippets-api/lib/db/db-client.ts +219 -4
- package/fake-snippets-api/lib/db/schema.ts +63 -1
- package/fake-snippets-api/lib/db/seed.ts +100 -0
- package/fake-snippets-api/lib/middleware/with-session-auth.ts +60 -8
- package/fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor.ts +2 -2
- package/fake-snippets-api/lib/public-mapping/public-map-org.ts +33 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +10 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +17 -0
- package/fake-snippets-api/routes/api/orgs/add_member.ts +52 -0
- package/fake-snippets-api/routes/api/orgs/create.ts +48 -0
- package/fake-snippets-api/routes/api/orgs/get.ts +39 -0
- package/fake-snippets-api/routes/api/orgs/list.ts +31 -0
- package/fake-snippets-api/routes/api/orgs/list_members.ts +60 -0
- package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
- package/fake-snippets-api/routes/api/orgs/update.ts +118 -0
- package/fake-snippets-api/routes/api/package_files/get.ts +3 -6
- package/fake-snippets-api/routes/api/package_files/list.ts +7 -4
- package/fake-snippets-api/routes/api/packages/create.ts +57 -10
- package/fake-snippets-api/routes/api/packages/get.ts +23 -0
- package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +13 -11
- package/fake-snippets-api/routes/api/packages/list.ts +29 -2
- package/fake-snippets-api/routes/api/packages/update_ai_description.ts +37 -0
- package/package.json +25 -19
- package/renovate.json +1 -1
- package/scripts/generate-sitemap.ts +1 -1
- package/src/App.tsx +27 -8
- package/src/ContextProviders.tsx +25 -2
- package/src/components/CircuitJsonImportDialog.tsx +1 -1
- package/src/components/CmdKMenu.tsx +281 -247
- package/src/components/DownloadButtonAndMenu.tsx +17 -5
- package/src/components/FileSidebar.tsx +11 -17
- package/src/components/Footer.tsx +8 -9
- package/src/components/Header.tsx +19 -32
- package/src/components/Header2.tsx +16 -32
- package/src/components/HeaderDropdown.tsx +13 -8
- package/src/components/HeaderLogin.tsx +43 -15
- package/src/components/NotFound.tsx +5 -5
- package/src/components/PackageBreadcrumb.tsx +6 -12
- package/src/components/PackageSearchResults.tsx +1 -1
- package/src/components/PrefetchPageLink.tsx +7 -1
- package/src/components/ProfileRouter.tsx +32 -0
- package/src/components/SearchComponent.tsx +12 -8
- package/src/components/SentryNotFoundReporter.tsx +44 -0
- package/src/components/UserCard.tsx +80 -0
- package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
- package/src/components/ViewPackagePage/components/important-files-view.tsx +105 -34
- package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -6
- package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -1
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -19
- package/src/components/ViewPackagePage/components/package-header.tsx +25 -33
- package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -18
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +12 -5
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
- package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
- package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
- package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
- package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
- package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
- package/src/components/dialogs/import-component-dialog.tsx +16 -9
- package/src/components/dialogs/import-package-dialog.tsx +3 -2
- package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
- package/src/components/organization/OrganizationCard.tsx +206 -0
- package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
- package/src/components/organization/OrganizationHeader.tsx +154 -0
- package/src/components/organization/OrganizationMembers.tsx +146 -0
- package/src/components/package-port/CodeAndPreview.tsx +15 -12
- package/src/components/package-port/CodeEditor.tsx +4 -30
- package/src/components/package-port/CodeEditorHeader.tsx +123 -61
- package/src/components/package-port/EditorNav.tsx +32 -49
- package/src/components/preview/ConnectedPackagesList.tsx +8 -8
- package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
- package/src/components/preview/PackageReleasesDashboard.tsx +23 -11
- package/src/components/ui/tree-view.tsx +6 -3
- package/src/hooks/use-add-org-member-mutation.ts +51 -0
- package/src/hooks/use-create-org-mutation.ts +38 -0
- package/src/hooks/use-create-package-mutation.ts +3 -0
- package/src/hooks/use-current-package-release.ts +4 -3
- package/src/hooks/use-download-zip.ts +2 -2
- package/src/hooks/use-global-store.ts +6 -4
- package/src/hooks/use-hydration.ts +30 -0
- package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
- package/src/hooks/use-list-org-members.ts +27 -0
- package/src/hooks/use-list-user-orgs.ts +25 -0
- package/src/hooks/use-org-by-github-handle.ts +26 -0
- package/src/hooks/use-org.ts +24 -0
- package/src/hooks/use-organization.ts +42 -0
- package/src/hooks/use-package-as-snippet.ts +4 -2
- package/src/hooks/use-package-builds.ts +6 -2
- package/src/hooks/use-package-files.ts +5 -3
- package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
- package/src/hooks/use-package-release-images.ts +105 -0
- package/src/hooks/use-package-release.ts +2 -2
- package/src/hooks/use-package-stars.ts +80 -4
- package/src/hooks/use-preview-images.ts +6 -3
- package/src/hooks/use-remove-org-member-mutation.ts +32 -0
- package/src/hooks/use-update-ai-description-mutation.ts +42 -0
- package/src/hooks/use-update-org-mutation.ts +41 -0
- package/src/hooks/use-warn-user-on-page-change.ts +71 -4
- package/src/hooks/useFileManagement.ts +51 -22
- package/src/hooks/useOptimizedPackageFilesLoader.ts +11 -24
- package/src/hooks/usePackageFilesLoader.ts +2 -2
- package/src/hooks/useUpdatePackageFilesMutation.ts +13 -1
- package/src/lib/download-fns/download-gltf-from-circuit-json.ts +1 -1
- package/src/lib/download-fns/download-kicad-files.ts +22 -11
- package/src/lib/download-fns/download-step.ts +12 -0
- package/src/lib/normalize-svg-for-tile.ts +50 -0
- package/src/lib/posthog.ts +11 -9
- package/src/lib/react-query-api-failure-tracking.ts +148 -0
- package/src/lib/sentry.ts +14 -0
- package/src/lib/templates/blank-circuit-board-template.ts +0 -4
- package/src/lib/ts-lib-cache.ts +122 -7
- package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
- package/src/lib/utils/findTargetFile.ts +45 -10
- package/src/lib/utils/isComponentExported.ts +2 -1
- package/src/main.tsx +2 -1
- package/src/pages/create-organization.tsx +169 -0
- package/src/pages/dashboard.tsx +38 -6
- package/src/pages/datasheet.tsx +1 -1
- package/src/pages/datasheets.tsx +3 -3
- package/src/pages/editor.tsx +4 -6
- package/src/pages/landing.tsx +6 -6
- package/src/pages/latest.tsx +3 -0
- package/src/pages/organization-profile.tsx +199 -0
- package/src/pages/organization-settings.tsx +569 -0
- package/src/pages/package-editor.tsx +21 -21
- package/src/pages/preview-release.tsx +75 -145
- package/src/pages/quickstart.tsx +159 -123
- package/src/pages/release-detail.tsx +119 -31
- package/src/pages/search.tsx +197 -57
- package/src/pages/settings-redirect.tsx +44 -0
- package/src/pages/trending.tsx +29 -20
- package/src/pages/user-profile.tsx +58 -7
- package/src/pages/user-settings.tsx +161 -0
- package/src/pages/view-package.tsx +30 -16
- package/vite.config.ts +9 -0
- package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
- package/src/components/JLCPCBImportDialog.tsx +0 -280
- package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -113
- package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -56
- package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
- package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
- package/src/components/PageSearchComponent.tsx +0 -148
- package/src/pages/package-builds.tsx +0 -33
|
@@ -11,12 +11,12 @@ jobs:
|
|
|
11
11
|
publish:
|
|
12
12
|
runs-on: ubuntu-latest
|
|
13
13
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
15
|
- name: Setup bun
|
|
16
|
-
uses: oven-sh/setup-bun@
|
|
16
|
+
uses: oven-sh/setup-bun@v2
|
|
17
17
|
with:
|
|
18
18
|
bun-version: latest
|
|
19
|
-
- uses: actions/setup-node@
|
|
19
|
+
- uses: actions/setup-node@v5
|
|
20
20
|
with:
|
|
21
21
|
node-version: 20
|
|
22
22
|
registry-url: https://registry.npmjs.org/
|
package/README.md
CHANGED
|
@@ -62,6 +62,10 @@ VITE_USE_DIRECT_AI_REQUESTS=true
|
|
|
62
62
|
VITE_ANTHROPIC_API_KEY=<your-key-here>
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
+
### Error Reporting
|
|
66
|
+
|
|
67
|
+
To enable Sentry error monitoring, set the `SENTRY_DSN` environment variable before running the app. The value will be exposed to the client as `VITE_SENTRY_DSN` and used to initialize Sentry.
|
|
68
|
+
|
|
65
69
|
### Building for Production
|
|
66
70
|
|
|
67
71
|
To build the project for production:
|
package/api/generated-index.js
CHANGED
|
@@ -36,7 +36,7 @@ const PREFETCHABLE_PAGES = new Set([
|
|
|
36
36
|
|
|
37
37
|
const pageDescriptions = {
|
|
38
38
|
landing:
|
|
39
|
-
"Build electronics with code
|
|
39
|
+
"Build electronics with code and AI tools. Render code into schematics, PCBs, 3D, fabrication files, and more. Open-source MIT licensed electronic design automation tool.",
|
|
40
40
|
dashboard:
|
|
41
41
|
"Your tscircuit dashboard - manage your electronic circuit packages, view trending and latest packages, and access your recent designs.",
|
|
42
42
|
search:
|
|
@@ -182,6 +182,9 @@ async function handleCustomPackageHtml(req, res) {
|
|
|
182
182
|
const [_, author, unscopedPackageName, other] = req.url
|
|
183
183
|
.split("?")[0]
|
|
184
184
|
.split("/")
|
|
185
|
+
if (unscopedPackageName === "settings") {
|
|
186
|
+
throw new Error("Organization settings route")
|
|
187
|
+
}
|
|
185
188
|
if (other == "releases" || other == "release") {
|
|
186
189
|
throw new Error("Release route")
|
|
187
190
|
}
|
|
@@ -240,8 +243,8 @@ async function handleCustomPackageHtml(req, res) {
|
|
|
240
243
|
if (packageRelease?.package_release_id) {
|
|
241
244
|
try {
|
|
242
245
|
const filesResponse = await ky
|
|
243
|
-
.
|
|
244
|
-
|
|
246
|
+
.get(`${REGISTRY_URL}/package_files/list`, {
|
|
247
|
+
searchParams: {
|
|
245
248
|
package_release_id: packageRelease.package_release_id,
|
|
246
249
|
},
|
|
247
250
|
})
|
|
@@ -289,6 +292,30 @@ async function handleCustomPackageHtml(req, res) {
|
|
|
289
292
|
res.setHeader("Vary", "Accept-Encoding")
|
|
290
293
|
res.status(200).send(html)
|
|
291
294
|
}
|
|
295
|
+
|
|
296
|
+
async function handleOrganizationSettings(req, res) {
|
|
297
|
+
const [_, orgSlug, settings] = req.url.split("?")[0].split("/")
|
|
298
|
+
if (!orgSlug || settings !== "settings") {
|
|
299
|
+
throw new Error("Not an organization settings route")
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const encodedSlugForUrl = encodeURIComponent(orgSlug)
|
|
303
|
+
const title = he.encode(`${orgSlug} Settings - tscircuit`)
|
|
304
|
+
const description = he.encode(
|
|
305
|
+
`Manage the ${orgSlug} organization settings on tscircuit.`,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
const html = getHtmlWithModifiedSeoTags({
|
|
309
|
+
title,
|
|
310
|
+
description,
|
|
311
|
+
canonicalUrl: he.encode(`${BASE_URL}/${encodedSlugForUrl}/settings`),
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8")
|
|
315
|
+
res.setHeader("Cache-Control", cacheControlHeader)
|
|
316
|
+
res.setHeader("Vary", "Accept-Encoding")
|
|
317
|
+
res.status(200).send(html)
|
|
318
|
+
}
|
|
292
319
|
async function handleCustomPage(req, res) {
|
|
293
320
|
const [_, page] = req.url.split("?")[0].split("/")
|
|
294
321
|
|
|
@@ -400,6 +427,13 @@ export default async function handler(req, res) {
|
|
|
400
427
|
}
|
|
401
428
|
}
|
|
402
429
|
|
|
430
|
+
try {
|
|
431
|
+
await handleOrganizationSettings(req, res)
|
|
432
|
+
return
|
|
433
|
+
} catch (e) {
|
|
434
|
+
console.warn(e)
|
|
435
|
+
}
|
|
436
|
+
|
|
403
437
|
try {
|
|
404
438
|
await handleCustomPage(req, res)
|
|
405
439
|
return
|
package/biome.json
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { afterEach } from "bun:test"
|
|
2
1
|
import defaultAxios from "redaxios"
|
|
3
2
|
import { startServer } from "./start-server"
|
|
4
3
|
import { DbClient } from "fake-snippets-api/lib/db/db-client"
|
|
@@ -40,7 +39,7 @@ export const getTestServer = async (): Promise<TestFixture> => {
|
|
|
40
39
|
baseURL: url,
|
|
41
40
|
})
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
globalThis.deferredCleanupFns.push(async () => {
|
|
44
43
|
if (server && typeof server.stop === "function") {
|
|
45
44
|
await server.stop()
|
|
46
45
|
}
|
|
@@ -73,6 +72,22 @@ const seedDatabase = (db: DbClient) => {
|
|
|
73
72
|
phone: "555-123-4567",
|
|
74
73
|
},
|
|
75
74
|
})
|
|
75
|
+
|
|
76
|
+
// Create personal organization for the main test account
|
|
77
|
+
const personalOrg = db.addOrganization({
|
|
78
|
+
org_id: "personal-org-1",
|
|
79
|
+
name: "testuser",
|
|
80
|
+
owner_account_id: account.account_id,
|
|
81
|
+
is_personal_org: true,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Add the account as a member of their personal org
|
|
85
|
+
db.addOrganizationAccount({
|
|
86
|
+
org_id: personalOrg.org_id,
|
|
87
|
+
account_id: account.account_id,
|
|
88
|
+
is_owner: true,
|
|
89
|
+
})
|
|
90
|
+
|
|
76
91
|
const account2 = db.addAccount({
|
|
77
92
|
github_username: "jane",
|
|
78
93
|
})
|
|
@@ -108,5 +123,19 @@ const seedDatabase = (db: DbClient) => {
|
|
|
108
123
|
transpilation_error: null,
|
|
109
124
|
})
|
|
110
125
|
|
|
111
|
-
|
|
126
|
+
// Seed a organization for account2
|
|
127
|
+
const organization = db.addOrganization({
|
|
128
|
+
name: "jane",
|
|
129
|
+
github_handle: "jane",
|
|
130
|
+
owner_account_id: account2.account_id,
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// Add account2 as a member of their org
|
|
134
|
+
db.addOrganizationAccount({
|
|
135
|
+
org_id: organization.org_id,
|
|
136
|
+
account_id: account2.account_id,
|
|
137
|
+
is_owner: true,
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
return { account, account2, order, packageRelease, organization }
|
|
112
141
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { afterEach } from "bun:test"
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
// Add the property to the globalThis type
|
|
5
|
+
// eslint-disable-next-line no-var
|
|
6
|
+
var deferredCleanupFns: Array<() => Promise<void>>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if (!globalThis.deferredCleanupFns) {
|
|
10
|
+
globalThis.deferredCleanupFns = []
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
for (const fn of globalThis.deferredCleanupFns) {
|
|
15
|
+
await fn()
|
|
16
|
+
}
|
|
17
|
+
globalThis.deferredCleanupFns = []
|
|
18
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
|
|
2
|
+
import { expect, test } from "bun:test"
|
|
3
|
+
|
|
4
|
+
test("POST /api/orgs/add_member - should add a user to an org (owner authorized)", async () => {
|
|
5
|
+
const { jane_axios, db, seed } = await getTestServer()
|
|
6
|
+
|
|
7
|
+
const originalMember = db.getAccount(seed.account.account_id)
|
|
8
|
+
|
|
9
|
+
const addResponse = await jane_axios.post("/api/orgs/add_member", {
|
|
10
|
+
org_id: seed.organization.org_id,
|
|
11
|
+
account_id: seed.account.account_id,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
expect(addResponse.status).toBe(200)
|
|
15
|
+
expect(addResponse.data).toEqual({})
|
|
16
|
+
|
|
17
|
+
expect(db.getAccount(seed.account.account_id)?.personal_org_id).toEqual(
|
|
18
|
+
String(originalMember?.personal_org_id),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const membership = db.getOrganizationAccount({
|
|
22
|
+
account_id: seed.account.account_id,
|
|
23
|
+
org_id: seed.organization.org_id,
|
|
24
|
+
})
|
|
25
|
+
expect(membership).toBeDefined()
|
|
26
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
|
|
2
|
+
import { expect, test } from "bun:test"
|
|
3
|
+
|
|
4
|
+
test("POST /api/orgs/create - should create a new org for the user", async () => {
|
|
5
|
+
const { axios } = await getTestServer()
|
|
6
|
+
const orgName = "acme-corp"
|
|
7
|
+
const createResponse = await axios.post("/api/orgs/create", {
|
|
8
|
+
name: orgName,
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
expect(createResponse.status).toBe(200)
|
|
12
|
+
const responseBody = createResponse.data
|
|
13
|
+
expect(responseBody.org).toBeDefined()
|
|
14
|
+
expect(responseBody.org.name).toBe(orgName)
|
|
15
|
+
expect(responseBody.org.owner_account_id).toBe(
|
|
16
|
+
String((axios.defaults.headers as any)?.["Authorization"]?.split(" ")[1]),
|
|
17
|
+
)
|
|
18
|
+
expect(responseBody.org.member_count).toBe(1)
|
|
19
|
+
expect(responseBody.org.package_count).toBe(0)
|
|
20
|
+
expect(responseBody.org.user_permissions?.can_manage_org).toBe(true)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test("POST /api/orgs/create - should reject duplicate org names", async () => {
|
|
24
|
+
const { axios, seed } = await getTestServer()
|
|
25
|
+
try {
|
|
26
|
+
await axios.post("/api/orgs/create", {
|
|
27
|
+
name: seed.organization.org_name,
|
|
28
|
+
})
|
|
29
|
+
throw new Error("Expected request to fail")
|
|
30
|
+
} catch (error: any) {
|
|
31
|
+
expect(error.status).toBe(400)
|
|
32
|
+
expect(error.data.error.error_code).toBe("org_already_exists")
|
|
33
|
+
expect(error.data.error.message).toBe(
|
|
34
|
+
"An organization with this name already exists",
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
|
|
2
|
+
import { expect, test } from "bun:test"
|
|
3
|
+
|
|
4
|
+
test("GET /api/orgs/get - should return org by org_id", async () => {
|
|
5
|
+
const { axios, jane_axios, seed } = await getTestServer()
|
|
6
|
+
|
|
7
|
+
const getResponse = await jane_axios.get("/api/orgs/get", {
|
|
8
|
+
params: { org_id: seed.organization.org_id },
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const getNotOwnerResponse = await axios.get("/api/orgs/get", {
|
|
12
|
+
params: { org_id: seed.organization.org_id },
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
expect(getResponse.status).toBe(200)
|
|
16
|
+
const responseBody = getResponse.data
|
|
17
|
+
const responseBody2 = getNotOwnerResponse.data
|
|
18
|
+
expect(responseBody.org).toBeDefined()
|
|
19
|
+
expect(responseBody.org.org_id).toBe(seed.organization.org_id)
|
|
20
|
+
expect(responseBody.org.name).toBe(seed.organization.github_handle)
|
|
21
|
+
expect(responseBody.org.user_permissions?.can_manage_org).toBe(true)
|
|
22
|
+
expect(responseBody2.org.user_permissions?.can_manage_org).not.toBe(true)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test("GET /api/orgs/get - should return org by github_handle", async () => {
|
|
26
|
+
const { axios } = await getTestServer()
|
|
27
|
+
|
|
28
|
+
const getResponse = await axios.get("/api/orgs/get", {
|
|
29
|
+
params: { github_handle: "jane" },
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
expect(getResponse.status).toBe(200)
|
|
33
|
+
const responseBody = getResponse.data
|
|
34
|
+
expect(responseBody.org).toBeDefined()
|
|
35
|
+
expect(responseBody.org.name).toBe("jane")
|
|
36
|
+
expect(responseBody.org.user_permissions?.can_manage_org).not.toBe(true)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test("GET /api/orgs/get - should return 404 if org not found", async () => {
|
|
40
|
+
const { axios } = await getTestServer()
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await axios.get("/api/orgs/get", {
|
|
44
|
+
params: { org_id: "non_existent_org_id" },
|
|
45
|
+
})
|
|
46
|
+
throw new Error("Expected request to fail")
|
|
47
|
+
} catch (error: any) {
|
|
48
|
+
expect(error.status).toBe(404)
|
|
49
|
+
expect(error.data.error.error_code).toBe("org_not_found")
|
|
50
|
+
expect(error.data.error.message).toBe("Organization not found")
|
|
51
|
+
}
|
|
52
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
|
|
2
|
+
import { expect, test } from "bun:test"
|
|
3
|
+
|
|
4
|
+
test("GET /api/orgs/list - should return user's organizations when authenticated", async () => {
|
|
5
|
+
const { jane_axios } = await getTestServer()
|
|
6
|
+
|
|
7
|
+
const listResponse = await jane_axios.get("/api/orgs/list")
|
|
8
|
+
|
|
9
|
+
expect(listResponse.status).toBe(200)
|
|
10
|
+
const responseBody = listResponse.data
|
|
11
|
+
expect(responseBody.ok).toBe(true)
|
|
12
|
+
expect(Array.isArray(responseBody.orgs)).toBe(true)
|
|
13
|
+
const personalOrg = responseBody.orgs[0]
|
|
14
|
+
expect(personalOrg).toBeDefined()
|
|
15
|
+
expect(personalOrg.name).toBe("jane")
|
|
16
|
+
expect(personalOrg.user_permissions?.can_manage_org).toBe(true)
|
|
17
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
|
|
2
|
+
import { expect, test } from "bun:test"
|
|
3
|
+
|
|
4
|
+
test("GET /orgs/list_members returns members for an org when owner", async () => {
|
|
5
|
+
const { jane_axios, seed } = await getTestServer()
|
|
6
|
+
|
|
7
|
+
await jane_axios.post("/api/orgs/add_member", {
|
|
8
|
+
org_id: seed.organization.org_id,
|
|
9
|
+
account_id: seed.account.account_id,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
data: { members },
|
|
14
|
+
} = await jane_axios.get(
|
|
15
|
+
`/api/orgs/list_members?org_id=${seed.organization.org_id}`,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
expect(Array.isArray(members)).toBe(true)
|
|
19
|
+
const membership = members.find(
|
|
20
|
+
(m: any) => m.account_id === seed.account.account_id,
|
|
21
|
+
)
|
|
22
|
+
expect(membership).toBeDefined()
|
|
23
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
|
|
2
|
+
import { expect, test } from "bun:test"
|
|
3
|
+
|
|
4
|
+
test("POST /api/orgs/remove_member - should remove a user from an org (resets to personal org)", async () => {
|
|
5
|
+
const { jane_axios, seed } = await getTestServer()
|
|
6
|
+
|
|
7
|
+
const createResponse = await jane_axios.post("/api/orgs/create", {
|
|
8
|
+
name: "globex",
|
|
9
|
+
})
|
|
10
|
+
const org = createResponse.data.org
|
|
11
|
+
|
|
12
|
+
await jane_axios.post("/api/orgs/add_member", {
|
|
13
|
+
org_id: org.org_id,
|
|
14
|
+
account_id: seed.account.account_id,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const removeResponse = await jane_axios.post("/api/orgs/remove_member", {
|
|
18
|
+
org_id: org.org_id,
|
|
19
|
+
account_id: seed.account.account_id,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
expect(removeResponse.status).toBe(200)
|
|
23
|
+
expect(removeResponse.data).toEqual({})
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test("POST /api/orgs/remove_member - should fail for non-owner (403)", async () => {
|
|
27
|
+
const { jane_axios, axios, seed } = await getTestServer()
|
|
28
|
+
|
|
29
|
+
const createResponse = await jane_axios.post("/api/orgs/create", {
|
|
30
|
+
name: "initech",
|
|
31
|
+
})
|
|
32
|
+
const org = createResponse.data.org
|
|
33
|
+
|
|
34
|
+
await jane_axios.post("/api/orgs/add_member", {
|
|
35
|
+
org_id: org.org_id,
|
|
36
|
+
account_id: seed.account.account_id,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await axios.post("/api/orgs/remove_member", {
|
|
41
|
+
org_id: org.org_id,
|
|
42
|
+
account_id: seed.account2.account_id,
|
|
43
|
+
})
|
|
44
|
+
throw new Error("Expected request to fail")
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
expect(error.status).toBe(403)
|
|
47
|
+
expect(error.data.error.error_code).toBe("not_authorized")
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test("POST /api/orgs/remove_member - should remove a user from an org and reset to personal org", async () => {
|
|
52
|
+
const { jane_axios, db, seed } = await getTestServer()
|
|
53
|
+
const originalMember = db.getAccount(seed.account.account_id)
|
|
54
|
+
|
|
55
|
+
const addResponse = await jane_axios.post("/api/orgs/add_member", {
|
|
56
|
+
org_id: seed.organization.org_id,
|
|
57
|
+
account_id: seed.account.account_id,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
expect(addResponse.status).toBe(200)
|
|
61
|
+
const membership = db.getOrganizationAccount({
|
|
62
|
+
org_id: seed.organization.org_id,
|
|
63
|
+
account_id: seed.account.account_id,
|
|
64
|
+
})
|
|
65
|
+
expect(membership).toBeDefined()
|
|
66
|
+
|
|
67
|
+
await jane_axios.post("/api/orgs/remove_member", {
|
|
68
|
+
org_id: seed.organization.org_id,
|
|
69
|
+
account_id: seed.account.account_id,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
expect(db.getAccount(seed.account.account_id)?.personal_org_id).toEqual(
|
|
73
|
+
String(originalMember?.personal_org_id),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const membershipAfter = db.getOrganizationAccount({
|
|
77
|
+
org_id: seed.organization.org_id,
|
|
78
|
+
account_id: seed.account.account_id,
|
|
79
|
+
})
|
|
80
|
+
expect(membershipAfter).toBeUndefined()
|
|
81
|
+
})
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
|
|
2
|
+
import { expect, test } from "bun:test"
|
|
3
|
+
|
|
4
|
+
test("POST /api/orgs/update - should update org name when owner", async () => {
|
|
5
|
+
const { axios } = await getTestServer()
|
|
6
|
+
|
|
7
|
+
const createResponse = await axios.post("/api/orgs/create", {
|
|
8
|
+
name: "old-name",
|
|
9
|
+
})
|
|
10
|
+
const org = createResponse.data.org
|
|
11
|
+
|
|
12
|
+
const updateResponse = await axios.post("/api/orgs/update", {
|
|
13
|
+
org_id: org.org_id,
|
|
14
|
+
name: "new-name",
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
expect(updateResponse.status).toBe(200)
|
|
18
|
+
const responseBody = updateResponse.data
|
|
19
|
+
expect(responseBody.org).toBeDefined()
|
|
20
|
+
expect(responseBody.org.name).toBe("new-name")
|
|
21
|
+
expect(responseBody.org.user_permissions?.can_manage_org).toBe(true)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test("PATCH /api/orgs/update - should update org name using PATCH method", async () => {
|
|
25
|
+
const { axios } = await getTestServer()
|
|
26
|
+
|
|
27
|
+
const createResponse = await axios.post("/api/orgs/create", {
|
|
28
|
+
name: "patch-test",
|
|
29
|
+
})
|
|
30
|
+
const org = createResponse.data.org
|
|
31
|
+
|
|
32
|
+
const updateResponse = await axios.patch("/api/orgs/update", {
|
|
33
|
+
org_id: org.org_id,
|
|
34
|
+
name: "patch-updated",
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
expect(updateResponse.status).toBe(200)
|
|
38
|
+
expect(updateResponse.data.org.name).toBe("patch-updated")
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test("POST /api/orgs/update - should return current org when no changes provided", async () => {
|
|
42
|
+
const { axios } = await getTestServer()
|
|
43
|
+
|
|
44
|
+
const createResponse = await axios.post("/api/orgs/create", {
|
|
45
|
+
name: "no-change",
|
|
46
|
+
})
|
|
47
|
+
const org = createResponse.data.org
|
|
48
|
+
|
|
49
|
+
const updateResponse = await axios.post("/api/orgs/update", {
|
|
50
|
+
org_id: org.org_id,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
expect(updateResponse.status).toBe(200)
|
|
54
|
+
expect(updateResponse.data.org.name).toBe("no-change")
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test("POST /api/orgs/update - should fail when user lacks management permissions", async () => {
|
|
58
|
+
const { axios, seed } = await getTestServer()
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
await axios.post("/api/orgs/update", {
|
|
62
|
+
org_id: seed.organization.org_id,
|
|
63
|
+
name: "unauthorized-change",
|
|
64
|
+
})
|
|
65
|
+
throw new Error("Expected request to fail")
|
|
66
|
+
} catch (error: any) {
|
|
67
|
+
expect(error.status).toBe(403)
|
|
68
|
+
expect(error.data.error.error_code).toBe("not_authorized")
|
|
69
|
+
expect(error.data.error.message).toBe(
|
|
70
|
+
"You do not have permission to manage this organization",
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test("POST /api/orgs/update - should reject duplicate name", async () => {
|
|
76
|
+
const { axios } = await getTestServer()
|
|
77
|
+
|
|
78
|
+
await axios.post("/api/orgs/create", {
|
|
79
|
+
name: "dup-a",
|
|
80
|
+
})
|
|
81
|
+
const org2Response = await axios.post("/api/orgs/create", {
|
|
82
|
+
name: "dup-b",
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const updateResponse = await axios.post("/api/orgs/update", {
|
|
87
|
+
org_id: org2Response.data.org.org_id,
|
|
88
|
+
name: "dup-a",
|
|
89
|
+
})
|
|
90
|
+
console.log(2, updateResponse.data)
|
|
91
|
+
throw new Error("Expected request to fail")
|
|
92
|
+
} catch (error: any) {
|
|
93
|
+
expect(error.status).toBe(400)
|
|
94
|
+
expect(error.data.error.error_code).toBe("org_already_exists")
|
|
95
|
+
expect(error.data.error.message).toBe(
|
|
96
|
+
"An organization with this name already exists",
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test("POST /api/orgs/update - should update github_handle when owner", async () => {
|
|
102
|
+
const { axios, db } = await getTestServer()
|
|
103
|
+
|
|
104
|
+
const createResponse = await axios.post("/api/orgs/create", {
|
|
105
|
+
name: "handle-owner",
|
|
106
|
+
})
|
|
107
|
+
const org = createResponse.data.org
|
|
108
|
+
|
|
109
|
+
const updateResponse = await axios.post("/api/orgs/update", {
|
|
110
|
+
org_id: org.org_id,
|
|
111
|
+
github_handle: "handle-owner",
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
expect(updateResponse.status).toBe(200)
|
|
115
|
+
|
|
116
|
+
const state = db.getState()
|
|
117
|
+
const updatedOrg = state.organizations.find(
|
|
118
|
+
(o: any) => o.org_id === org.org_id,
|
|
119
|
+
)
|
|
120
|
+
expect(updatedOrg?.github_handle).toBe("handle-owner")
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test("POST /api/orgs/update - should reject duplicate github_handle", async () => {
|
|
124
|
+
const { axios } = await getTestServer()
|
|
125
|
+
|
|
126
|
+
const orgAResponse = await axios.post("/api/orgs/create", {
|
|
127
|
+
name: "dup-handle-a",
|
|
128
|
+
})
|
|
129
|
+
const orgA = orgAResponse.data.org
|
|
130
|
+
|
|
131
|
+
await axios.post("/api/orgs/update", {
|
|
132
|
+
org_id: orgA.org_id,
|
|
133
|
+
github_handle: "duplicate-handle",
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const orgBResponse = await axios.post("/api/orgs/create", {
|
|
137
|
+
name: "dup-handle-b",
|
|
138
|
+
})
|
|
139
|
+
const orgB = orgBResponse.data.org
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
await axios.post("/api/orgs/update", {
|
|
143
|
+
org_id: orgB.org_id,
|
|
144
|
+
github_handle: "duplicate-handle",
|
|
145
|
+
})
|
|
146
|
+
throw new Error("Expected request to fail")
|
|
147
|
+
} catch (error: any) {
|
|
148
|
+
expect(error.status).toBe(400)
|
|
149
|
+
expect(error.data.error.error_code).toBe("org_github_handle_already_exists")
|
|
150
|
+
}
|
|
151
|
+
})
|
|
@@ -24,7 +24,7 @@ export const createTestPackageRelease = async (
|
|
|
24
24
|
return releaseRes.data
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
test("GET /api/package_builds/get - requires package_build_id
|
|
27
|
+
test("GET /api/package_builds/get - requires package_build_id", async () => {
|
|
28
28
|
const { jane_axios } = await getTestServer()
|
|
29
29
|
const res = await jane_axios.get("/api/package_builds/get", {
|
|
30
30
|
validateStatus: () => true,
|