11import { describe , expect , it } from 'vitest' ;
2- import { parseSpacesQueryResult } from '../../src/space/find-many-public.js' ;
2+ import { buildFilterString , buildSpacesQuery , parseSpacesQueryResult } from '../../src/space/find-many-public.js' ;
33
44const buildQuerySpace = ( {
55 id = 'space-id' ,
6+ type = 'PERSONAL' ,
67 name = 'Space name' ,
78 avatar,
89 editorsList = [ ] ,
910 membersList = [ ] ,
1011} : {
1112 id ?: string ;
13+ type ?: 'PERSONAL' | 'DAO' ;
1214 name ?: string | null ;
1315 avatar ?: string | null ;
1416 editorsList ?: { memberSpaceId : string } [ ] ;
1517 membersList ?: { memberSpaceId : string } [ ] ;
1618} = { } ) => {
1719 return {
1820 id,
21+ type,
1922 page : {
2023 name,
2124 relationsList :
@@ -51,6 +54,7 @@ describe('parseSpacesQueryResult', () => {
5154 expect ( data ) . toEqual ( [
5255 {
5356 id : 'space-1' ,
57+ type : 'PERSONAL' ,
5458 name : 'Space 1' ,
5559 avatar : 'https://example.com/avatar.png' ,
5660 editorIds : [ ] ,
@@ -68,13 +72,30 @@ describe('parseSpacesQueryResult', () => {
6872 expect ( data ) . toEqual ( [
6973 {
7074 id : 'space-2' ,
75+ type : 'PERSONAL' ,
7176 name : 'Space 2' ,
7277 editorIds : [ ] ,
7378 memberIds : [ ] ,
7479 } ,
7580 ] ) ;
7681 } ) ;
7782
83+ it ( 'parses DAO type' , ( ) => {
84+ const { data } = parseSpacesQueryResult ( {
85+ spaces : [ buildQuerySpace ( { id : 'space-dao' , type : 'DAO' , name : 'DAO Space' } ) ] ,
86+ } ) ;
87+
88+ expect ( data ) . toEqual ( [
89+ {
90+ id : 'space-dao' ,
91+ type : 'DAO' ,
92+ name : 'DAO Space' ,
93+ editorIds : [ ] ,
94+ memberIds : [ ] ,
95+ } ,
96+ ] ) ;
97+ } ) ;
98+
7899 it ( 'filters invalid data' , ( ) => {
79100 const { data, invalidSpaces } = parseSpacesQueryResult ( {
80101 spaces : [
@@ -86,6 +107,7 @@ describe('parseSpacesQueryResult', () => {
86107 expect ( data ) . toEqual ( [
87108 {
88109 id : 'space-valid' ,
110+ type : 'PERSONAL' ,
89111 name : 'Space valid' ,
90112 avatar : 'https://example.com/a.png' ,
91113 editorIds : [ ] ,
@@ -111,10 +133,117 @@ describe('parseSpacesQueryResult', () => {
111133 expect ( data ) . toEqual ( [
112134 {
113135 id : 'space-with-members' ,
136+ type : 'PERSONAL' ,
114137 name : 'Space with members' ,
115138 editorIds : [ 'editor-1' , 'editor-2' ] ,
116139 memberIds : [ 'member-1' ] ,
117140 } ,
118141 ] ) ;
119142 } ) ;
120143} ) ;
144+
145+ describe ( 'buildFilterString' , ( ) => {
146+ it ( 'returns undefined when no filter is provided' , ( ) => {
147+ expect ( buildFilterString ( ) ) . toBeUndefined ( ) ;
148+ expect ( buildFilterString ( { } ) ) . toBeUndefined ( ) ;
149+ } ) ;
150+
151+ it ( 'builds filter string with memberId' , ( ) => {
152+ const result = buildFilterString ( { memberId : '1e5e39daa00d4fd8b53b98095337112f' } ) ;
153+ expect ( result ) . toBe ( 'filter: {members: {some: {memberSpaceId: {is: "1e5e39daa00d4fd8b53b98095337112f"}}}}' ) ;
154+ } ) ;
155+
156+ it ( 'builds filter string with editorId' , ( ) => {
157+ const result = buildFilterString ( { editorId : '1e5e39daa00d4fd8b53b98095337112f' } ) ;
158+ expect ( result ) . toBe ( 'filter: {editors: {some: {memberSpaceId: {is: "1e5e39daa00d4fd8b53b98095337112f"}}}}' ) ;
159+ } ) ;
160+
161+ it ( 'builds filter string with spaceType PERSONAL' , ( ) => {
162+ const result = buildFilterString ( { spaceType : 'PERSONAL' } ) ;
163+ expect ( result ) . toBe ( 'filter: {type: {is: PERSONAL}}' ) ;
164+ } ) ;
165+
166+ it ( 'builds filter string with spaceType DAO' , ( ) => {
167+ const result = buildFilterString ( { spaceType : 'DAO' } ) ;
168+ expect ( result ) . toBe ( 'filter: {type: {is: DAO}}' ) ;
169+ } ) ;
170+
171+ it ( 'builds filter string with memberId and spaceType' , ( ) => {
172+ const result = buildFilterString ( { memberId : '1e5e39daa00d4fd8b53b98095337112f' , spaceType : 'PERSONAL' } ) ;
173+ expect ( result ) . toBe (
174+ 'filter: {members: {some: {memberSpaceId: {is: "1e5e39daa00d4fd8b53b98095337112f"}}}, type: {is: PERSONAL}}' ,
175+ ) ;
176+ } ) ;
177+
178+ it ( 'builds filter string with editorId and spaceType' , ( ) => {
179+ const result = buildFilterString ( { editorId : '1e5e39daa00d4fd8b53b98095337112f' , spaceType : 'DAO' } ) ;
180+ expect ( result ) . toBe (
181+ 'filter: {editors: {some: {memberSpaceId: {is: "1e5e39daa00d4fd8b53b98095337112f"}}}, type: {is: DAO}}' ,
182+ ) ;
183+ } ) ;
184+
185+ it ( 'normalizes UUID with dashes to dashless format' , ( ) => {
186+ const result = buildFilterString ( { memberId : '1e5e39da-a00d-4fd8-b53b-98095337112f' } ) ;
187+ expect ( result ) . toBe ( 'filter: {members: {some: {memberSpaceId: {is: "1e5e39daa00d4fd8b53b98095337112f"}}}}' ) ;
188+ } ) ;
189+
190+ it ( 'throws error for invalid memberId' , ( ) => {
191+ expect ( ( ) => buildFilterString ( { memberId : 'invalid-id' } ) ) . toThrow ( 'Invalid Geo ID' ) ;
192+ } ) ;
193+
194+ it ( 'throws error for invalid editorId' , ( ) => {
195+ expect ( ( ) => buildFilterString ( { editorId : 'invalid"; DROP TABLE spaces; --' } ) ) . toThrow ( 'Invalid Geo ID' ) ;
196+ } ) ;
197+
198+ it ( 'throws error for invalid spaceType' , ( ) => {
199+ // @ts -expect-error - testing runtime validation with invalid value
200+ expect ( ( ) => buildFilterString ( { spaceType : 'INVALID' } ) ) . toThrow (
201+ "Invalid spaceType: INVALID. Must be 'PERSONAL' or 'DAO'." ,
202+ ) ;
203+ } ) ;
204+ } ) ;
205+
206+ describe ( 'buildSpacesQuery' , ( ) => {
207+ it ( 'builds query without filter' , ( ) => {
208+ const query = buildSpacesQuery ( ) ;
209+ expect ( query ) . toContain ( 'query spaces {' ) ;
210+ // Check that the top-level spaces query doesn't have a filter (spaces { not spaces(filter:)
211+ expect ( query ) . toMatch ( / s p a c e s \s * \{ / ) ;
212+ expect ( query ) . not . toMatch ( / s p a c e s \s * \( f i l t e r : / ) ;
213+ } ) ;
214+
215+ it ( 'builds query with memberId filter' , ( ) => {
216+ const query = buildSpacesQuery ( { memberId : '1e5e39daa00d4fd8b53b98095337112f' } ) ;
217+ expect ( query ) . toContain (
218+ 'spaces(filter: {members: {some: {memberSpaceId: {is: "1e5e39daa00d4fd8b53b98095337112f"}}}})' ,
219+ ) ;
220+ } ) ;
221+
222+ it ( 'builds query with editorId filter' , ( ) => {
223+ const query = buildSpacesQuery ( { editorId : '1e5e39daa00d4fd8b53b98095337112f' } ) ;
224+ expect ( query ) . toContain (
225+ 'spaces(filter: {editors: {some: {memberSpaceId: {is: "1e5e39daa00d4fd8b53b98095337112f"}}}})' ,
226+ ) ;
227+ } ) ;
228+
229+ it ( 'builds query with spaceType filter only' , ( ) => {
230+ const query = buildSpacesQuery ( { spaceType : 'DAO' } ) ;
231+ expect ( query ) . toContain ( 'spaces(filter: {type: {is: DAO}})' ) ;
232+ } ) ;
233+
234+ it ( 'builds query with combined filters' , ( ) => {
235+ const query = buildSpacesQuery ( { memberId : '1e5e39daa00d4fd8b53b98095337112f' , spaceType : 'PERSONAL' } ) ;
236+ expect ( query ) . toContain (
237+ 'spaces(filter: {members: {some: {memberSpaceId: {is: "1e5e39daa00d4fd8b53b98095337112f"}}}, type: {is: PERSONAL}})' ,
238+ ) ;
239+ } ) ;
240+
241+ it ( 'includes required space fields in query' , ( ) => {
242+ const query = buildSpacesQuery ( ) ;
243+ expect ( query ) . toContain ( 'id' ) ;
244+ expect ( query ) . toContain ( 'type' ) ;
245+ expect ( query ) . toContain ( 'page {' ) ;
246+ expect ( query ) . toContain ( 'editorsList {' ) ;
247+ expect ( query ) . toContain ( 'membersList {' ) ;
248+ } ) ;
249+ } ) ;
0 commit comments