summaryrefslogtreecommitdiff
path: root/HutchTests/ProjectFormTests.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-08-07 04:12:50 -0500
committerChristian Cleberg <[email protected]>2026-08-07 04:12:50 -0500
commit1f8f651a9c2ac2d6be231ff9f71ee3bcce9936d8 (patch)
tree25cf810fa0e9a0ab343b60b59c26d8cc096c9a98 /HutchTests/ProjectFormTests.swift
parentdb40d346c28800a1eec384a5e4f3d7d090198e4d (diff)
downloadhutch-1f8f651a9c2ac2d6be231ff9f71ee3bcce9936d8.tar.gz
hutch-1f8f651a9c2ac2d6be231ff9f71ee3bcce9936d8.tar.bz2
hutch-1f8f651a9c2ac2d6be231ff9f71ee3bcce9936d8.zip
Projects: discovery, create, edit, manage linked resources (#12, #13, #14, #15)
hub.sr.ht's GraphQL now exposes the project write API (create/update/ link/unlink) and a public `projects` discovery query, so these previously-blocked issues can be built. ProjectService gains: fetchPublicProjects (#12); createProject (#13); updateProject with ProjectInput (#14); link/unlink for sources, trackers, and mailing lists plus linkable-resource candidate fetches (#15); and hub cache invalidation after every mutation. UI: a Discover browser and a create form reached from the projects list; an Edit form and a Manage Resources sheet on project detail, gated to projects the user owns. Mutations degrade to a visible error if a field isn't live yet. Built against the master schema; live deployment on sr.ht/query is not yet confirmed (introspection there requires a token).
Diffstat (limited to 'HutchTests/ProjectFormTests.swift')
-rw-r--r--HutchTests/ProjectFormTests.swift39
1 files changed, 39 insertions, 0 deletions
diff --git a/HutchTests/ProjectFormTests.swift b/HutchTests/ProjectFormTests.swift
new file mode 100644
index 0000000..a4b4dfa
--- /dev/null
+++ b/HutchTests/ProjectFormTests.swift
@@ -0,0 +1,39 @@
+import Foundation
+import Testing
+@testable import Hutch
+
+struct ProjectFormTests {
+ @Test
+ func parsesTagsSplittingAndTrimming() {
+ #expect(ProjectFormSheet.parseTags("swift, ios , ") == ["swift", "ios"])
+ #expect(ProjectFormSheet.parseTags("a,b,c") == ["a", "b", "c"])
+ }
+
+ @Test
+ func parseTagsDeduplicatesCaseInsensitively() {
+ #expect(ProjectFormSheet.parseTags("Swift, swift, SWIFT") == ["Swift"])
+ }
+
+ @Test
+ func parseTagsEmptyInputYieldsEmpty() {
+ #expect(ProjectFormSheet.parseTags(" ").isEmpty)
+ #expect(ProjectFormSheet.parseTags("").isEmpty)
+ }
+
+ @Test
+ func linkableResourceDisplayNameCombinesOwnerAndName() {
+ let resource = LinkableResource(rid: "r1", name: "hutch", ownerCanonicalName: "~alice", kind: .source)
+ #expect(resource.displayName == "~alice/hutch")
+ #expect(resource.id == "r1")
+ }
+
+ @Test
+ func discoveredProjectIDMatchesProject() {
+ let project = Project(
+ metadata: .init(id: "p1", name: "Hutch", description: nil, website: nil, visibility: .publicVisibility, tags: [], updated: .now),
+ resources: .init(mailingLists: [], sources: [], trackers: [], isFullyLoaded: false)
+ )
+ let discovered = DiscoveredProject(project: project, ownerCanonicalName: "~alice")
+ #expect(discovered.id == "p1")
+ }
+}