1+ import { getAreaModel , createIndexes } from "../../db"
2+ import inMemoryDB from "../../utils/inMemoryDB"
3+ import MutableAreaDataSource from "../MutableAreaDataSource"
4+ import muid , { MUUID } from 'uuid-mongodb'
5+ import { AreaType , OperationType } from "../../db/AreaTypes"
6+ import { ChangeRecordMetadataType } from "../../db/ChangeLogType"
7+ import { UserInputError } from "apollo-server-core"
8+ import mongoose from "mongoose"
9+ import exp from "constants"
10+
11+
12+ describe ( "Test area mutations" , ( ) => {
13+ let areas : MutableAreaDataSource
14+ let rootCountry : AreaType
15+ let areaCounter = 0
16+ const testUser = muid . v4 ( )
17+
18+ async function addArea ( name ?: string , extra ?: Partial < { leaf : boolean , boulder : boolean , parent : MUUID | AreaType } > ) {
19+ function isArea ( x : any ) : x is AreaType {
20+ return typeof x . metadata ?. area_id !== 'undefined'
21+ }
22+
23+ areaCounter += 1
24+ if ( name === undefined || name === 'test' ) {
25+ name = process . uptime ( ) . toString ( ) + '-' + areaCounter . toString ( )
26+ }
27+
28+ let parent : MUUID | undefined = undefined
29+ if ( extra ?. parent ) {
30+ if ( isArea ( extra . parent ) ) {
31+ parent = extra . parent . metadata ?. area_id
32+ } else {
33+ parent = extra . parent
34+ }
35+ }
36+
37+ return areas . addArea (
38+ testUser ,
39+ name ,
40+ parent ?? rootCountry . metadata . area_id ,
41+ undefined ,
42+ undefined ,
43+ extra ?. leaf ,
44+ extra ?. boulder
45+ )
46+ }
47+
48+ beforeAll ( async ( ) => {
49+ await inMemoryDB . connect ( )
50+ await getAreaModel ( ) . collection . drop ( )
51+ await createIndexes ( )
52+
53+ areas = MutableAreaDataSource . getInstance ( )
54+ // We need a root country, and it is beyond the scope of these tests
55+ rootCountry = await areas . addCountry ( "USA" )
56+ } )
57+
58+ afterAll ( inMemoryDB . close )
59+
60+ describe ( "Add area param cases" , ( ) => {
61+ test ( "Add a simple area with no specifications using a parent UUID" , ( ) => areas
62+ . addArea ( testUser , 'Texas2' , rootCountry . metadata . area_id )
63+ . then ( area => {
64+ expect ( area ?. _change ) . toMatchObject ( {
65+ user : testUser ,
66+ operation : OperationType . addArea ,
67+ } satisfies Partial < ChangeRecordMetadataType > )
68+ } ) )
69+
70+ test ( "Add an area with an unknown UUID parent should fail" ,
71+ async ( ) => await expect ( ( ) => areas . addArea ( testUser , 'Texas' , muid . v4 ( ) ) ) . rejects . toThrow ( ) )
72+
73+ test ( "Add a simple area with no specifications using a country code" , ( ) => areas . addArea ( testUser , 'Texas part 2' , null , 'USA' )
74+ . then ( texas => areas . addArea ( testUser , 'Texas Child' , texas . metadata . area_id ) ) )
75+
76+ test ( "Add a simple area, then specify a new child one level deep" , ( ) => addArea ( 'California' )
77+ . then ( async parent => {
78+ let child = await addArea ( 'Child' , { parent } )
79+ expect ( child ) . toMatchObject ( { area_name : 'Child' } )
80+ let parentCheck = await areas . findOneAreaByUUID ( parent . metadata . area_id )
81+ expect ( parentCheck ?. children ?? [ ] ) . toContainEqual ( child . _id )
82+ } ) )
83+
84+ test ( "Add a leaf area" , ( ) => addArea ( 'Somewhere' ) . then ( parent => addArea ( 'Child' , { leaf : true , parent } ) )
85+ . then ( async leaf => {
86+ expect ( leaf ) . toMatchObject ( { metadata : { leaf : true } } )
87+ let area = await areas . areaModel . findById ( leaf . _id )
88+ expect ( area ) . toMatchObject ( { metadata : { leaf : true } } )
89+ } ) )
90+
91+ test ( "Add a leaf area that is a boulder" , ( ) => addArea ( 'Maine' )
92+ . then ( parent => addArea ( 'Child' , { leaf : true , boulder : true , parent} ) )
93+ . then ( area => {
94+ expect ( area ) . toMatchObject ( {
95+ metadata : {
96+ leaf : true ,
97+ isBoulder : true ,
98+ } ,
99+ } satisfies Partial < Omit < AreaType , 'metadata' > & { metadata : Partial < AreaType [ 'metadata' ] > } > )
100+ } ) )
101+
102+ test ( "Add a NON-leaf area that is a boulder" , ( ) => addArea ( 'Wisconcin' )
103+ . then ( texas => addArea ( 'Child' , { leaf : false , boulder : true } ) )
104+ . then ( area => {
105+ expect ( area ) . toMatchObject ( {
106+ metadata : {
107+ // Even though we specified false to leaf on the input, we expect it to be true
108+ // after write because a boulder cannot contain sub-areas
109+ leaf : true ,
110+ isBoulder : true ,
111+ } ,
112+ } satisfies Partial < Omit < AreaType , 'metadata' > & { metadata : Partial < AreaType [ 'metadata' ] > } > )
113+ } ) )
114+
115+ test ( "Adding a child to a leaf area should cause it to become a normal area" , ( ) => addArea ( )
116+ . then ( parent => Promise . all ( new Array ( 5 ) . map ( ( ) => addArea ( 'test' , { leaf : true , parent } ) ) ) )
117+ . then ( ( [ leaf ] ) => leaf )
118+ . then ( leaf => addArea ( 'test' , { parent : leaf } ) )
119+ . then ( leaf => expect ( leaf ) . toMatchObject ( { metadata : { leaf : false } } ) ) )
120+
121+ test ( "area names should be unique in their parent context" , ( ) => addArea ( 'test' ) . then ( async parent => {
122+ await addArea ( 'Big ol boulder' , { parent } )
123+ await expect ( ( ) => addArea ( 'Big ol boulder' , { parent } ) ) . rejects . toThrowError ( UserInputError )
124+ } ) )
125+ } )
126+
127+ test ( "Delete Area" , ( ) => addArea ( "test" ) . then ( area => areas . deleteArea ( testUser , area . metadata . area_id ) ) . then ( async deleted => {
128+ expect ( deleted ) . toBeDefined ( )
129+ // TODO: this test fails based on the data returned, which appears to omit the _deleting field.
130+ let d = await areas . areaModel . findById ( deleted ?. _id )
131+
132+ expect ( d ) . toBeDefined ( )
133+ expect ( d ) . not . toBeNull ( )
134+ expect ( d ?. _deleting ) . toBeDefined ( )
135+ } ) )
136+
137+ test ( "Delete Area that is already deleted should throw" , ( ) => addArea ( "test" )
138+ . then ( area => areas . deleteArea ( testUser , area . metadata . area_id ) )
139+ . then ( async area => {
140+ expect ( area ) . not . toBeNull ( )
141+ await expect ( ( ) => areas . deleteArea ( testUser , area ! . metadata . area_id ) ) . rejects . toThrow ( )
142+ } ) )
143+
144+
145+
146+ describe ( "Area update cases" , ( ) => {
147+ test ( "Updating an area should superficially pass" , ( ) => addArea ( 'test' ) . then ( area => areas . updateArea ( testUser , area . metadata . area_id , { areaName : `New Name! ${ process . uptime ( ) } ` } ) ) )
148+ test ( "Updating an area should produce a change entry in the changelog" , ( ) => addArea ( 'test' )
149+ . then ( area => areas . updateArea ( testUser , area . metadata . area_id , { areaName : process . uptime ( ) . toString ( ) } ) )
150+ . then ( area => {
151+ expect ( area ?. _change ) . toMatchObject ( {
152+ user : testUser ,
153+ operation : OperationType . updateArea ,
154+ } satisfies Partial < ChangeRecordMetadataType > )
155+ } ) )
156+
157+ test ( "Area name uniqueness in its current parent context" , ( ) => addArea ( 'test' ) . then ( async parent => {
158+ let [ area , newArea , divorcedArea ] = await Promise . all ( [
159+ addArea ( 'original' , { parent } ) ,
160+ addArea ( 'wannabe' , { parent } ) ,
161+ addArea ( undefined , { parent : rootCountry } ) ,
162+ ] )
163+
164+ await Promise . all ( [
165+ // Case where an area gets changed to what it already is, which should not throw an error
166+ areas . updateArea ( testUser , area . metadata . area_id , { areaName : area . area_name } ) ,
167+ // name-uniqueness should not be global, so this shouldn't throw
168+ areas . updateArea ( testUser , divorcedArea . metadata . area_id , { areaName : area . area_name } ) ,
169+ // if we update one of the areas to have a name for which another area already exists, we should expect this to throw.
170+ expect ( ( ) => areas . updateArea ( testUser , newArea . metadata . area_id , { areaName : area . area_name } ) ) . rejects . toThrowError ( UserInputError ) ,
171+ ] )
172+ } ) )
173+ } )
174+
175+ test ( "Area name uniqueness should not create a UUID shadow via deletion" , ( ) => addArea ( 'test' ) . then ( async parent => {
176+ let name = 'Big ol boulder'
177+ let big = await addArea ( name , { boulder : true , parent } )
178+ await areas . deleteArea ( testUser , big . metadata . area_id )
179+ await addArea ( name , { boulder : true , parent } )
180+ } ) )
181+
182+ test ( "Area name uniqueness should not create a UUID shadow via edit of name" , ( ) => addArea ( 'test' ) . then ( async parent => {
183+ let nameShadow = 'Big ol boulder 2'
184+ let big = await addArea ( nameShadow , { boulder : true , parent } )
185+
186+ // We change the name of the original owner of the nameshadow, and then try to add a
187+ // name claming the original name in this area structure context
188+ await areas . updateArea ( testUser , big . metadata . area_id , { areaName : "Still big ol bolder" } )
189+ await addArea ( nameShadow , { boulder : true , parent } )
190+ } ) )
191+ } )
0 commit comments