|
1 | 1 | import { MetadataStorage } from '../../src/metadata/MetadataStorage'; |
| 2 | +import { ConstraintMetadata } from '../../src/metadata/ConstraintMetadata'; |
2 | 3 | import { ValidationMetadata } from '../../src/metadata/ValidationMetadata'; |
| 4 | +import { ValidationSchema } from '../../src/validation-schema/ValidationSchema'; |
3 | 5 | import { ValidationTypes } from '../../src/validation/ValidationTypes'; |
4 | 6 |
|
5 | 7 | describe('MetadataStorage PR-2665 coverage', () => { |
@@ -94,4 +96,175 @@ describe('MetadataStorage PR-2665 coverage', () => { |
94 | 96 | expect(strictProperties).toContain('always'); |
95 | 97 | expect(strictProperties).not.toContain('grouped'); |
96 | 98 | }); |
| 99 | + |
| 100 | + it('should cache grouped metadata and reuse the same object for a cache key', () => { |
| 101 | + const storage = new MetadataStorage(); |
| 102 | + const metadata = [createMetadata(ValidationTypes.CUSTOM_VALIDATION, 'prop')]; |
| 103 | + |
| 104 | + const first = storage.groupByPropertyName(metadata, 'group-cache-key'); |
| 105 | + const second = storage.groupByPropertyName([], 'group-cache-key'); |
| 106 | + |
| 107 | + expect(second).toBe(first); |
| 108 | + expect(second.prop).toHaveLength(1); |
| 109 | + }); |
| 110 | + |
| 111 | + it('should append constraint metadata for the same target', () => { |
| 112 | + class TestConstraint {} |
| 113 | + |
| 114 | + const storage = new MetadataStorage(); |
| 115 | + storage.addConstraintMetadata(new ConstraintMetadata(TestConstraint, 'first')); |
| 116 | + storage.addConstraintMetadata(new ConstraintMetadata(TestConstraint, 'second')); |
| 117 | + |
| 118 | + expect(storage.getTargetValidatorConstraints(TestConstraint)).toHaveLength(2); |
| 119 | + }); |
| 120 | + |
| 121 | + it('should cache target metadata results and include inherited grouped metadata', () => { |
| 122 | + class ParentTarget {} |
| 123 | + class ChildTarget extends ParentTarget {} |
| 124 | + |
| 125 | + const storage = new MetadataStorage(); |
| 126 | + const inheritedGrouped = new ValidationMetadata({ |
| 127 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 128 | + target: ParentTarget, |
| 129 | + propertyName: 'inheritedGrouped', |
| 130 | + }); |
| 131 | + inheritedGrouped.groups = ['g1']; |
| 132 | + |
| 133 | + storage.addValidationMetadata(inheritedGrouped); |
| 134 | + |
| 135 | + const cacheKey = storage.buildCacheKey(ChildTarget, '', false, false, ['g1']); |
| 136 | + const first = storage.getTargetValidationMetadatas(ChildTarget, '', false, false, ['g1'], cacheKey); |
| 137 | + const second = storage.getTargetValidationMetadatas(ChildTarget, '', false, false, ['g1'], cacheKey); |
| 138 | + |
| 139 | + expect(second).toBe(first); |
| 140 | + expect(first.map(metadata => metadata.propertyName)).toContain('inheritedGrouped'); |
| 141 | + }); |
| 142 | + |
| 143 | + it('should ignore inherited metadata whose target is not actually in the prototype chain', () => { |
| 144 | + class ParentTarget {} |
| 145 | + class ChildTarget extends ParentTarget {} |
| 146 | + class RogueTarget {} |
| 147 | + |
| 148 | + const storage = new MetadataStorage(); |
| 149 | + const rogueMetadata = new ValidationMetadata({ |
| 150 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 151 | + target: RogueTarget, |
| 152 | + propertyName: 'rogue', |
| 153 | + }); |
| 154 | + |
| 155 | + (storage as any).validationMetadatas = new Map([[ParentTarget, [rogueMetadata]]]); |
| 156 | + |
| 157 | + expect(storage.getTargetValidationMetadatas(ChildTarget, '', false, false)).toEqual([]); |
| 158 | + }); |
| 159 | + |
| 160 | + it('should transform validation schemas into metadata entries', () => { |
| 161 | + const storage = new MetadataStorage(); |
| 162 | + const schema: ValidationSchema = { |
| 163 | + name: 'SchemaTarget', |
| 164 | + properties: { |
| 165 | + field: [ |
| 166 | + { |
| 167 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 168 | + name: 'schemaConstraint', |
| 169 | + constraints: ['x'], |
| 170 | + message: 'schema message', |
| 171 | + }, |
| 172 | + ], |
| 173 | + }, |
| 174 | + }; |
| 175 | + |
| 176 | + storage.addValidationSchema(schema); |
| 177 | + |
| 178 | + const metadatas = (storage as any).validationMetadatas.get('SchemaTarget'); |
| 179 | + expect(metadatas).toHaveLength(1); |
| 180 | + expect(metadatas[0].name).toEqual('schemaConstraint'); |
| 181 | + expect(metadatas[0].propertyName).toEqual('field'); |
| 182 | + }); |
| 183 | + |
| 184 | + it('should ignore original metadata entries whose target does not match the constructor or schema', () => { |
| 185 | + class ActualTarget {} |
| 186 | + class OtherTarget {} |
| 187 | + |
| 188 | + const storage = new MetadataStorage(); |
| 189 | + const mismatched = new ValidationMetadata({ |
| 190 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 191 | + target: OtherTarget, |
| 192 | + propertyName: 'mismatch', |
| 193 | + }); |
| 194 | + |
| 195 | + (storage as any).validationMetadatas = new Map([[ActualTarget, [mismatched]]]); |
| 196 | + |
| 197 | + expect(storage.getTargetValidationMetadatas(ActualTarget, '', false, false)).toEqual([]); |
| 198 | + }); |
| 199 | + |
| 200 | + it('should exclude string and self-targeted inherited metadata', () => { |
| 201 | + class ParentTarget {} |
| 202 | + class ChildTarget extends ParentTarget {} |
| 203 | + |
| 204 | + const storage = new MetadataStorage(); |
| 205 | + const schemaInherited = new ValidationMetadata({ |
| 206 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 207 | + target: 'SchemaTarget', |
| 208 | + propertyName: 'schemaInherited', |
| 209 | + }); |
| 210 | + const selfInherited = new ValidationMetadata({ |
| 211 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 212 | + target: ChildTarget, |
| 213 | + propertyName: 'selfInherited', |
| 214 | + }); |
| 215 | + const validInherited = new ValidationMetadata({ |
| 216 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 217 | + target: ParentTarget, |
| 218 | + propertyName: 'validInherited', |
| 219 | + }); |
| 220 | + validInherited.always = true; |
| 221 | + |
| 222 | + (storage as any).validationMetadatas = new Map([[ParentTarget, [schemaInherited, selfInherited, validInherited]]]); |
| 223 | + |
| 224 | + const metadatas = storage.getTargetValidationMetadatas(ChildTarget, '', false, true); |
| 225 | + |
| 226 | + expect(metadatas.map(metadata => metadata.propertyName)).toEqual(['validInherited']); |
| 227 | + }); |
| 228 | + |
| 229 | + it('should prefer original metadata over inherited metadata with the same property and type', () => { |
| 230 | + class ParentTarget {} |
| 231 | + class ChildTarget extends ParentTarget {} |
| 232 | + |
| 233 | + const storage = new MetadataStorage(); |
| 234 | + const original = new ValidationMetadata({ |
| 235 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 236 | + target: ChildTarget, |
| 237 | + propertyName: 'shared', |
| 238 | + }); |
| 239 | + const inheritedDuplicate = new ValidationMetadata({ |
| 240 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 241 | + target: ParentTarget, |
| 242 | + propertyName: 'shared', |
| 243 | + }); |
| 244 | + |
| 245 | + storage.addValidationMetadata(original); |
| 246 | + storage.addValidationMetadata(inheritedDuplicate); |
| 247 | + |
| 248 | + const metadatas = storage.getTargetValidationMetadatas(ChildTarget, '', false, false); |
| 249 | + |
| 250 | + expect(metadatas).toHaveLength(1); |
| 251 | + expect(metadatas[0]).toBe(original); |
| 252 | + }); |
| 253 | + |
| 254 | + it('should exclude inherited grouped metadata when strictGroups is enabled without groups', () => { |
| 255 | + class ParentTarget {} |
| 256 | + class ChildTarget extends ParentTarget {} |
| 257 | + |
| 258 | + const storage = new MetadataStorage(); |
| 259 | + const inheritedGrouped = new ValidationMetadata({ |
| 260 | + type: ValidationTypes.CUSTOM_VALIDATION, |
| 261 | + target: ParentTarget, |
| 262 | + propertyName: 'inheritedGrouped', |
| 263 | + }); |
| 264 | + inheritedGrouped.groups = ['g1']; |
| 265 | + |
| 266 | + storage.addValidationMetadata(inheritedGrouped); |
| 267 | + |
| 268 | + expect(storage.getTargetValidationMetadatas(ChildTarget, '', false, true)).toEqual([]); |
| 269 | + }); |
97 | 270 | }); |
0 commit comments