Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 333 lines 9.0 kB view raw
1import { 2 ContainerTypes, 3 ScalarTypes, 4 type Field, 5 type FieldType, 6} from '@roostorg/types'; 7import _ from 'lodash'; 8 9import { type NonEmptyArray } from '../../utils/typescript-types.js'; 10import { 11 type ContentSchemaFieldRoles, 12 type ItemType, 13} from '../moderationConfigService/index.js'; 14import { toNormalizedItemDataOrErrors } from './toNormalizedItemDataOrErrors.js'; 15 16const { omit } = _; 17 18const fakeSchema = [ 19 { 20 name: 'missingRequired', 21 type: ScalarTypes.BOOLEAN, 22 required: true, 23 container: null, 24 }, 25 { 26 name: 'name', 27 type: ScalarTypes.STRING, 28 required: false, 29 container: null, 30 }, 31 { 32 name: 'num', 33 type: ScalarTypes.NUMBER, 34 required: false, 35 container: null, 36 }, 37 { 38 name: 'email', 39 required: true, 40 type: ScalarTypes.STRING, 41 container: null, 42 }, 43 { 44 name: 'optionalNull', 45 type: ScalarTypes.STRING, 46 required: false, 47 container: null, 48 }, 49 { 50 name: 'geohashInvalid', 51 type: ScalarTypes.GEOHASH, 52 required: false, 53 container: null, 54 }, 55 { 56 name: 'requiredNull', 57 type: ScalarTypes.STRING, 58 required: true, 59 container: null, 60 }, 61 { 62 name: 'requiredFalsey1', 63 type: ScalarTypes.BOOLEAN, 64 required: true, 65 container: null, 66 }, 67 { 68 name: 'requiredFalsey2', 69 type: ScalarTypes.NUMBER, 70 required: true, 71 container: null, 72 }, 73 { 74 name: 'requiredFalsey3', 75 type: ScalarTypes.STRING, 76 required: true, 77 container: null, 78 }, 79 { 80 name: 'containerWithInvalidItem', 81 type: ContainerTypes.ARRAY, 82 required: true, 83 container: { 84 containerType: ContainerTypes.ARRAY, 85 valueScalarType: ScalarTypes.STRING, 86 keyScalarType: null, 87 }, 88 }, 89 { 90 name: 'validContainer', 91 type: ContainerTypes.ARRAY, 92 required: true, 93 container: { 94 containerType: ContainerTypes.ARRAY, 95 valueScalarType: ScalarTypes.STRING, 96 keyScalarType: null, 97 }, 98 }, 99 { 100 name: 'relatedItem', 101 type: ScalarTypes.RELATED_ITEM, 102 required: true, 103 container: null, 104 }, 105 { 106 name: 'relatedItem2', 107 type: ScalarTypes.RELATED_ITEM, 108 required: true, 109 container: null, 110 }, 111 { 112 name: 'relatedItem3', 113 type: ScalarTypes.RELATED_ITEM, 114 required: true, 115 container: null, 116 }, 117] as const; 118 119const fakeUserSubmission = { 120 name: 'test', 121 // NB: we expect num to be valid, but not email. 122 num: '130', // should come out cast to a number in the snapshot. 123 email: 201, 124 optionalNull: null, 125 geohashInvalid: { 126 geometry: { 127 center: { 128 lat: 12, 129 lng: 12, 130 }, 131 radius: 3, 132 }, 133 }, 134 // these should all be valid, until requiredNUll. 135 requiredFalsey1: false, 136 requiredFalsey2: 0, 137 requiredFalsey3: '', 138 requiredNull: null, 139 containerWithInvalidItem: ['test', null], 140 validContainer: ['test'], 141 relatedItem: { 142 id: 'test', 143 typeId: 'testTypeId', 144 name: 'test name', 145 }, 146 relatedItem2: 'not a related item', 147 relatedItem3: { 148 id: 'test', 149 typeId: 'testTypeId', 150 name: { k: 'v' }, 151 }, 152}; 153 154const fakeSchemaWithFieldRoles = [ 155 { 156 name: 'createdAt', 157 type: ScalarTypes.STRING, 158 required: false, 159 container: null, 160 }, 161 { 162 name: 'threadId', 163 type: ScalarTypes.RELATED_ITEM, 164 required: false, 165 container: null, 166 }, 167 { 168 name: 'parentId', 169 type: ScalarTypes.RELATED_ITEM, 170 required: false, 171 container: null, 172 }, 173] as const; 174const fakeFieldRoles = { 175 createdAt: 'createdAt', 176 threadId: 'threadId', 177 parentId: 'parentId', 178} as const; 179 180describe('Content type schemas', () => { 181 describe('toNormalizedItemDataOrErrors', () => { 182 test('should give proper errors', () => { 183 expect( 184 toNormalizedItemDataOrErrors( 185 ['testTypeId'], 186 getFakeItemTypeFromSchema(fakeSchema), 187 fakeUserSubmission, 188 ), 189 ).toMatchInlineSnapshot(` 190 [ 191 [BadRequestError: Invalid Data for Item The field 'missingRequired' is required, but was not provided.], 192 [BadRequestError: Invalid Data for Item The field 'email' has an invalid value. The value you provided was: 201. This field, if given, must be a string.], 193 [BadRequestError: Invalid Data for Item The field 'geohashInvalid' has an invalid value. The value you provided was: {"geometry":{"center":{"lat":12,"lng":12},"radius":3}}. This field, if given, must be a valid geohash.], 194 [BadRequestError: Invalid Data for Item The field 'requiredNull' is required, but was not provided.], 195 [BadRequestError: Invalid Data for Item The field 'containerWithInvalidItem' has an invalid value. The value you provided was: ["test",null]. Some items in this field's array were not valid.], 196 [BadRequestError: Invalid Data for Item The field 'relatedItem2' has an invalid value. The value you provided was: "not a related item". This field, if given, must be an object with a (non-empty) string 'id' and a valid 'typeId', with an optional string name.], 197 [BadRequestError: Invalid Data for Item The field 'relatedItem3' has an invalid value. The value you provided was: {"id":"test","name":{"k":"v"},"typeId":"testTypeId"}. This field, if given, must be an object with a (non-empty) string 'id' and a valid 'typeId', with an optional string name.], 198 ] 199 `); 200 }); 201 202 test('should return the normalized version of valid submissions', () => { 203 const schemaFieldsToRemove = new Set([ 204 'missingRequired', 205 'requiredNull', 206 'containerWithInvalidItem', 207 'email', 208 'relatedItem2', 209 'relatedItem3', 210 ]); 211 212 const [validSubmission, validSubmissionSchema] = [ 213 omit(fakeUserSubmission, [ 214 'email', 215 'geohashInvalid', 216 'requiredNull', 217 'containerWithInvalidItem', 218 'relatedItem2', 219 'relatedItem3', 220 ]), 221 fakeSchema.filter( 222 (it) => !schemaFieldsToRemove.has(it.name), 223 ) as NonEmptyArray<(typeof fakeSchema)[number]>, 224 ]; 225 226 expect( 227 toNormalizedItemDataOrErrors( 228 ['testTypeId'], 229 getFakeItemTypeFromSchema(validSubmissionSchema), 230 validSubmission, 231 ), 232 ).toMatchInlineSnapshot(` 233 { 234 "name": "test", 235 "num": 130, 236 "relatedItem": { 237 "id": "test", 238 "name": "test name", 239 "typeId": "testTypeId", 240 }, 241 "requiredFalsey1": false, 242 "requiredFalsey2": 0, 243 "requiredFalsey3": "", 244 "validContainer": [ 245 "test", 246 ], 247 } 248 `); 249 }); 250 test('should fail for invalid field roles when only parentId is provided', () => { 251 expect( 252 toNormalizedItemDataOrErrors( 253 ['testTypeId'], 254 getFakeItemTypeFromSchema(fakeSchemaWithFieldRoles, fakeFieldRoles), 255 { 256 parentId: { 257 id: 'test', 258 typeId: 'testTypeId', 259 }, 260 }, 261 ), 262 ).toMatchInlineSnapshot(` 263 [ 264 [BadRequestError: Invalid field roles for Item You provided us a parent: parentId without providing a value for when the item was created: createdAt or a value for the thread: threadId], 265 ] 266 `); 267 expect( 268 toNormalizedItemDataOrErrors( 269 ['testTypeId'], 270 getFakeItemTypeFromSchema(fakeSchemaWithFieldRoles, fakeFieldRoles), 271 { 272 threadId: { 273 id: 'test', 274 typeId: 'testTypeId', 275 }, 276 }, 277 ), 278 ).toMatchInlineSnapshot(` 279 [ 280 [BadRequestError: Invalid field roles for Item You provided us a thread: threadId without providing a value for when the item was created: createdAt], 281 ] 282 `); 283 }); 284 test('should pass for valid field roles', () => { 285 expect( 286 toNormalizedItemDataOrErrors( 287 ['testTypeId'], 288 getFakeItemTypeFromSchema(fakeSchemaWithFieldRoles, fakeFieldRoles), 289 { 290 threadId: { 291 id: 'test', 292 typeId: 'testTypeId', 293 }, 294 parentId: { 295 id: 'test', 296 typeId: 'testTypeId', 297 }, 298 createdAt: '2023-04-12T19:47:09.406Z', 299 }, 300 ), 301 ).toMatchInlineSnapshot(` 302 { 303 "createdAt": "2023-04-12T19:47:09.406Z", 304 "parentId": { 305 "id": "test", 306 "typeId": "testTypeId", 307 }, 308 "threadId": { 309 "id": "test", 310 "typeId": "testTypeId", 311 }, 312 } 313 `); 314 }); 315 }); 316}); 317 318function getFakeItemTypeFromSchema( 319 schema: readonly [Field<FieldType>, ...Field<FieldType>[]], 320 schemaFieldRoles: ContentSchemaFieldRoles = {}, 321): ItemType { 322 return { 323 id: 'test', 324 kind: 'CONTENT', 325 name: 'test', 326 description: 'test', 327 version: 'test', 328 schemaVariant: 'original', 329 orgId: 'test orgId', 330 schemaFieldRoles, 331 schema, 332 }; 333}