@api-client/core 0.18.16 → 0.18.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/build/src/{modeling → decorators}/observed.d.ts +3 -3
  2. package/build/src/decorators/observed.d.ts.map +1 -0
  3. package/build/src/{modeling → decorators}/observed.js +4 -4
  4. package/build/src/decorators/observed.js.map +1 -0
  5. package/build/src/modeling/ApiModel.js +1 -1
  6. package/build/src/modeling/ApiModel.js.map +1 -1
  7. package/build/src/modeling/DataDomain.d.ts +7 -2
  8. package/build/src/modeling/DataDomain.d.ts.map +1 -1
  9. package/build/src/modeling/DataDomain.js +15 -2
  10. package/build/src/modeling/DataDomain.js.map +1 -1
  11. package/build/src/modeling/DomainAssociation.d.ts +7 -0
  12. package/build/src/modeling/DomainAssociation.d.ts.map +1 -1
  13. package/build/src/modeling/DomainAssociation.js +44 -1
  14. package/build/src/modeling/DomainAssociation.js.map +1 -1
  15. package/build/src/modeling/DomainEntity.d.ts +6 -0
  16. package/build/src/modeling/DomainEntity.d.ts.map +1 -1
  17. package/build/src/modeling/DomainEntity.js +21 -1
  18. package/build/src/modeling/DomainEntity.js.map +1 -1
  19. package/build/src/modeling/DomainModel.js +1 -1
  20. package/build/src/modeling/DomainModel.js.map +1 -1
  21. package/build/src/modeling/DomainNamespace.js +1 -1
  22. package/build/src/modeling/DomainNamespace.js.map +1 -1
  23. package/build/src/modeling/DomainProperty.d.ts +5 -0
  24. package/build/src/modeling/DomainProperty.d.ts.map +1 -1
  25. package/build/src/modeling/DomainProperty.js +38 -1
  26. package/build/src/modeling/DomainProperty.js.map +1 -1
  27. package/build/src/modeling/DomainSerialization.d.ts +6 -3
  28. package/build/src/modeling/DomainSerialization.d.ts.map +1 -1
  29. package/build/src/modeling/DomainSerialization.js +374 -52
  30. package/build/src/modeling/DomainSerialization.js.map +1 -1
  31. package/build/src/modeling/types.d.ts +69 -2
  32. package/build/src/modeling/types.d.ts.map +1 -1
  33. package/build/src/modeling/types.js.map +1 -1
  34. package/build/src/models/Thing.js +1 -1
  35. package/build/src/models/Thing.js.map +1 -1
  36. package/build/tsconfig.tsbuildinfo +1 -1
  37. package/data/models/example-generator-api.json +10 -10
  38. package/package.json +2 -1
  39. package/src/{modeling → decorators}/observed.ts +5 -5
  40. package/src/modeling/ApiModel.ts +1 -1
  41. package/src/modeling/DataDomain.ts +24 -3
  42. package/src/modeling/DomainAssociation.ts +51 -1
  43. package/src/modeling/DomainEntity.ts +24 -1
  44. package/src/modeling/DomainModel.ts +1 -1
  45. package/src/modeling/DomainNamespace.ts +1 -1
  46. package/src/modeling/DomainProperty.ts +43 -1
  47. package/src/modeling/DomainSerialization.ts +440 -56
  48. package/src/modeling/types.ts +73 -2
  49. package/src/models/Thing.ts +1 -1
  50. package/tests/unit/decorators/observed.spec.ts +527 -0
  51. package/tests/unit/modeling/data_domain_serialization.spec.ts +508 -0
  52. package/tests/unit/modeling/domain_asociation.spec.ts +376 -0
  53. package/tests/unit/modeling/domain_entity.spec.ts +147 -0
  54. package/tests/unit/modeling/domain_property.spec.ts +273 -0
  55. package/build/src/modeling/observed.d.ts.map +0 -1
  56. package/build/src/modeling/observed.js.map +0 -1
@@ -908,3 +908,379 @@ test.group('DomainAssociation.hasSemantic()', () => {
908
908
  assert.isFalse(association.hasSemantic(SemanticType.ResourceOwnerIdentifier))
909
909
  })
910
910
  })
911
+
912
+ test.group('DomainAssociation.duplicate()', () => {
913
+ test('duplicates an association successfully', ({ assert }) => {
914
+ const dataDomain = new DataDomain()
915
+ const model = dataDomain.addModel()
916
+ const entity = model.addEntity()
917
+ const targetEntity = model.addEntity()
918
+
919
+ const association = entity.addAssociation({ key: targetEntity.key })
920
+ association.info.name = 'originalAssociation'
921
+ association.required = true
922
+ association.multiple = false
923
+
924
+ const duplicate = association.duplicate()
925
+
926
+ assert.isDefined(duplicate)
927
+ assert.notEqual(duplicate.key, association.key)
928
+ assert.equal(duplicate.info.name, 'originalAssociation_copy')
929
+ assert.equal(duplicate.required, true)
930
+ assert.equal(duplicate.multiple, false)
931
+ })
932
+
933
+ test('places duplicate right after original association in fields list', ({ assert }) => {
934
+ const dataDomain = new DataDomain()
935
+ const model = dataDomain.addModel()
936
+ const entity = model.addEntity()
937
+ const targetEntity = model.addEntity()
938
+
939
+ // Add multiple fields to test ordering
940
+ const property1 = entity.addProperty({ info: { name: 'first' } })
941
+ const association1 = entity.addAssociation({ key: targetEntity.key })
942
+ association1.info.name = 'originalAssoc'
943
+ const property2 = entity.addProperty({ info: { name: 'second' } })
944
+
945
+ const duplicate = association1.duplicate()
946
+
947
+ // Check that duplicate is placed right after association1
948
+ const fieldKeys = entity.fields.map((f) => f.key)
949
+ const association1Index = fieldKeys.indexOf(association1.key)
950
+ const duplicateIndex = fieldKeys.indexOf(duplicate.key)
951
+
952
+ assert.equal(duplicateIndex, association1Index + 1)
953
+ assert.deepEqual(fieldKeys, [property1.key, association1.key, duplicate.key, property2.key])
954
+ })
955
+
956
+ test('generates unique name when base name conflicts', ({ assert }) => {
957
+ const dataDomain = new DataDomain()
958
+ const model = dataDomain.addModel()
959
+ const entity = model.addEntity()
960
+ const targetEntity = model.addEntity()
961
+
962
+ const association = entity.addAssociation({ key: targetEntity.key })
963
+ association.info.name = 'test'
964
+ // Add another association that conflicts with the first generated name
965
+ const conflictAssoc = entity.addAssociation({ key: targetEntity.key })
966
+ conflictAssoc.info.name = 'test_copy'
967
+
968
+ const duplicate = association.duplicate()
969
+
970
+ assert.equal(duplicate.info.name, 'test_copy_2')
971
+ })
972
+
973
+ test('uses fallback name when original association has no name', ({ assert }) => {
974
+ const dataDomain = new DataDomain()
975
+ const model = dataDomain.addModel()
976
+ const entity = model.addEntity()
977
+ const targetEntity = model.addEntity()
978
+
979
+ const association = entity.addAssociation({ key: targetEntity.key })
980
+ // The association gets the default name "new_association" from schema creation
981
+ assert.equal(association.info.name, 'new_association')
982
+
983
+ const duplicate = association.duplicate()
984
+
985
+ assert.equal(duplicate.info.name, 'new_association_copy')
986
+ })
987
+
988
+ test('uses field fallback when association name is explicitly undefined', ({ assert }) => {
989
+ const dataDomain = new DataDomain()
990
+ const model = dataDomain.addModel()
991
+ const entity = model.addEntity()
992
+ const targetEntity = model.addEntity()
993
+
994
+ const association = entity.addAssociation({ key: targetEntity.key })
995
+ association.info.name = 'test'
996
+ // Manually set name to undefined to test the fallback logic
997
+ association.info.name = undefined
998
+
999
+ const duplicate = association.duplicate()
1000
+
1001
+ assert.equal(duplicate.info.name, 'field_copy')
1002
+ })
1003
+
1004
+ test('does not copy key property', ({ assert }) => {
1005
+ const dataDomain = new DataDomain()
1006
+ const model = dataDomain.addModel()
1007
+ const entity = model.addEntity()
1008
+ const targetEntity = model.addEntity()
1009
+
1010
+ const association = entity.addAssociation({ key: targetEntity.key })
1011
+ const originalKey = association.key
1012
+
1013
+ const duplicate = association.duplicate()
1014
+
1015
+ assert.notEqual(duplicate.key, originalKey)
1016
+ })
1017
+
1018
+ test('does not copy targets initially but recreates them', ({ assert }) => {
1019
+ const dataDomain = new DataDomain()
1020
+ const model = dataDomain.addModel()
1021
+ const entity = model.addEntity()
1022
+ const targetEntity1 = model.addEntity()
1023
+ const targetEntity2 = model.addEntity()
1024
+
1025
+ const association = entity.addAssociation({ key: targetEntity1.key })
1026
+ association.addTarget(targetEntity2.key)
1027
+ association.info.name = 'multiTarget'
1028
+
1029
+ const duplicate = association.duplicate()
1030
+
1031
+ // Should have the same targets but different association instance
1032
+ const originalTargets = [...association.listTargets()]
1033
+ const duplicateTargets = [...duplicate.listTargets()]
1034
+
1035
+ assert.equal(originalTargets.length, duplicateTargets.length)
1036
+ assert.equal(originalTargets.length, 2)
1037
+
1038
+ // Check that target keys match
1039
+ const originalKeys = originalTargets.map((t) => t.key).sort()
1040
+ const duplicateKeys = duplicateTargets.map((t) => t.key).sort()
1041
+ assert.deepEqual(originalKeys, duplicateKeys)
1042
+ })
1043
+
1044
+ test('copies all other association attributes', ({ assert }) => {
1045
+ const dataDomain = new DataDomain()
1046
+ const model = dataDomain.addModel()
1047
+ const entity = model.addEntity()
1048
+ const targetEntity = model.addEntity()
1049
+
1050
+ const association = entity.addAssociation({ key: targetEntity.key })
1051
+ association.info.name = 'complexAssociation'
1052
+ association.info.description = 'A complex association'
1053
+ association.required = true
1054
+ association.multiple = true
1055
+ association.readOnly = true
1056
+ association.onDelete = 'cascade'
1057
+ association.semantics = [{ id: SemanticType.ResourceOwnerIdentifier }]
1058
+ association.schema = { linked: true, unionType: 'allOf' }
1059
+ association.bindings = [{ type: 'web', schema: { hidden: true } }]
1060
+
1061
+ const duplicate = association.duplicate()
1062
+
1063
+ assert.equal(duplicate.info.name, 'complexAssociation_copy')
1064
+ assert.equal(duplicate.info.description, 'A complex association')
1065
+ assert.equal(duplicate.required, true)
1066
+ assert.equal(duplicate.multiple, true)
1067
+ assert.equal(duplicate.readOnly, true)
1068
+ assert.equal(duplicate.onDelete, 'cascade')
1069
+ assert.deepEqual(duplicate.semantics, [{ id: SemanticType.ResourceOwnerIdentifier }])
1070
+ assert.deepEqual(duplicate.schema, { linked: true, unionType: 'allOf' })
1071
+ assert.deepEqual(duplicate.bindings, [{ type: 'web', schema: { hidden: true } }])
1072
+ })
1073
+
1074
+ test('creates independent copy of schema and bindings', ({ assert }) => {
1075
+ const dataDomain = new DataDomain()
1076
+ const model = dataDomain.addModel()
1077
+ const entity = model.addEntity()
1078
+ const targetEntity = model.addEntity()
1079
+
1080
+ const association = entity.addAssociation({ key: targetEntity.key })
1081
+ association.info.name = 'original'
1082
+ association.schema = { linked: false, unionType: 'anyOf' }
1083
+ association.bindings = [{ type: 'web', schema: { hidden: false } }]
1084
+
1085
+ const duplicate = association.duplicate()
1086
+
1087
+ // Modify original schema and bindings
1088
+ association.schema!.linked = true
1089
+ association.bindings[0].schema = { hidden: true }
1090
+
1091
+ // Duplicate should be unaffected
1092
+ assert.equal(duplicate.schema!.linked, false)
1093
+ assert.deepEqual(duplicate.bindings[0].schema, { hidden: false })
1094
+ })
1095
+
1096
+ test('copies targets correctly', ({ assert }) => {
1097
+ const dataDomain = new DataDomain()
1098
+ const model = dataDomain.addModel()
1099
+ const entity = model.addEntity()
1100
+
1101
+ const targetEntity1 = model.addEntity()
1102
+ const targetEntity2 = model.addEntity()
1103
+
1104
+ const association = entity.addAssociation()
1105
+ association.addTarget(targetEntity1.key)
1106
+ association.addTarget(targetEntity2.key)
1107
+ association.info.name = 'multiTargetAssoc'
1108
+
1109
+ // Check that original association has targets
1110
+ const originalTargets = [...association.listTargets()]
1111
+ assert.equal(originalTargets.length, 2)
1112
+
1113
+ const duplicate = association.duplicate()
1114
+
1115
+ // Check targets on duplicate
1116
+ const duplicateTargets = [...duplicate.listTargets()]
1117
+ const duplicateTargetObjects = duplicate.targets
1118
+
1119
+ // Check targets array structure
1120
+ assert.equal(duplicateTargetObjects.length, 2)
1121
+ assert.equal(duplicateTargetObjects[0].key, targetEntity1.key)
1122
+ assert.equal(duplicateTargetObjects[1].key, targetEntity2.key)
1123
+
1124
+ // Check resolved targets
1125
+ assert.equal(duplicateTargets.length, 2)
1126
+ const duplicateKeys = duplicateTargets.map((t) => t.key).sort()
1127
+ const expectedKeys = [targetEntity1.key, targetEntity2.key].sort()
1128
+ assert.deepEqual(duplicateKeys, expectedKeys)
1129
+ })
1130
+
1131
+ test('notifies domain of changes', ({ assert }) => {
1132
+ const dataDomain = new DataDomain()
1133
+ const model = dataDomain.addModel()
1134
+ const entity = model.addEntity()
1135
+ const targetEntity = model.addEntity()
1136
+
1137
+ const association = entity.addAssociation({ key: targetEntity.key })
1138
+ association.info.name = 'test'
1139
+
1140
+ let notificationCalled = false
1141
+ const originalNotify = dataDomain.notifyChange
1142
+ dataDomain.notifyChange = () => {
1143
+ notificationCalled = true
1144
+ originalNotify.call(dataDomain)
1145
+ }
1146
+
1147
+ association.duplicate()
1148
+
1149
+ assert.isTrue(notificationCalled)
1150
+
1151
+ // Restore original method
1152
+ dataDomain.notifyChange = originalNotify
1153
+ })
1154
+
1155
+ test('throws error when association has no parent entity', ({ assert }) => {
1156
+ const dataDomain = new DataDomain()
1157
+ const association = new DomainAssociation(dataDomain, 'non-existent-parent', {
1158
+ info: { name: 'orphan' },
1159
+ })
1160
+
1161
+ assert.throws(() => {
1162
+ association.duplicate()
1163
+ }, 'Cannot duplicate association')
1164
+ })
1165
+
1166
+ test('throws error when association is not in parent entity fields list', ({ assert }) => {
1167
+ const dataDomain = new DataDomain()
1168
+ const model = dataDomain.addModel()
1169
+ const entity = model.addEntity()
1170
+ const association = new DomainAssociation(dataDomain, entity.key, {
1171
+ info: { name: 'detached' },
1172
+ })
1173
+
1174
+ // Add association to graph but not to entity fields
1175
+ dataDomain.graph.setNode(association.key, association)
1176
+
1177
+ assert.throws(() => {
1178
+ association.duplicate()
1179
+ }, 'does not exist on the parent entity fields list')
1180
+ })
1181
+
1182
+ test('works with empty entity fields list', ({ assert }) => {
1183
+ const dataDomain = new DataDomain()
1184
+ const model = dataDomain.addModel()
1185
+ const entity = model.addEntity()
1186
+ const targetEntity = model.addEntity()
1187
+ const association = entity.addAssociation({ key: targetEntity.key })
1188
+ association.info.name = 'only'
1189
+
1190
+ const duplicate = association.duplicate()
1191
+
1192
+ assert.equal(entity.fields.length, 2)
1193
+ assert.equal(entity.fields[0].key, association.key)
1194
+ assert.equal(entity.fields[1].key, duplicate.key)
1195
+ })
1196
+
1197
+ test('handles duplication with mixed field types', ({ assert }) => {
1198
+ const dataDomain = new DataDomain()
1199
+ const model = dataDomain.addModel()
1200
+ const entity = model.addEntity()
1201
+ const targetEntity = model.addEntity()
1202
+
1203
+ const property1 = entity.addProperty({ info: { name: 'prop1' } })
1204
+ const association1 = entity.addAssociation({ key: targetEntity.key })
1205
+ association1.info.name = 'assoc1'
1206
+ const property2 = entity.addProperty({ info: { name: 'prop2' } })
1207
+
1208
+ const duplicate = association1.duplicate()
1209
+
1210
+ // Should maintain correct order
1211
+ const fieldKeys = entity.fields.map((f) => f.key)
1212
+ assert.deepEqual(fieldKeys, [property1.key, association1.key, duplicate.key, property2.key])
1213
+ })
1214
+
1215
+ test('preserves field types correctly', ({ assert }) => {
1216
+ const dataDomain = new DataDomain()
1217
+ const model = dataDomain.addModel()
1218
+ const entity = model.addEntity()
1219
+ const targetEntity = model.addEntity()
1220
+ const association = entity.addAssociation({ key: targetEntity.key })
1221
+
1222
+ const duplicate = association.duplicate()
1223
+
1224
+ // Check that both original and duplicate have correct field type
1225
+ const originalField = entity.fields.find((f) => f.key === association.key)
1226
+ const duplicateField = entity.fields.find((f) => f.key === duplicate.key)
1227
+
1228
+ assert.equal(originalField?.type, 'association')
1229
+ assert.equal(duplicateField?.type, 'association')
1230
+ })
1231
+
1232
+ test('handles multiple target entities correctly', ({ assert }) => {
1233
+ const dataDomain = new DataDomain()
1234
+ const model = dataDomain.addModel()
1235
+ const entity = model.addEntity()
1236
+ const targetEntity1 = model.addEntity()
1237
+ const targetEntity2 = model.addEntity()
1238
+ const targetEntity3 = model.addEntity()
1239
+
1240
+ const association = entity.addAssociation()
1241
+ association.addTarget(targetEntity1.key)
1242
+ association.addTarget(targetEntity2.key)
1243
+ association.addTarget(targetEntity3.key)
1244
+ association.info.name = 'multiTarget'
1245
+
1246
+ const duplicate = association.duplicate()
1247
+
1248
+ const originalTargets = [...association.listTargets()]
1249
+ const duplicateTargets = [...duplicate.listTargets()]
1250
+
1251
+ assert.equal(originalTargets.length, 3)
1252
+ assert.equal(duplicateTargets.length, 3)
1253
+
1254
+ // Verify all targets are copied correctly
1255
+ const originalKeys = originalTargets.map((t) => t.key).sort()
1256
+ const duplicateKeys = duplicateTargets.map((t) => t.key).sort()
1257
+ assert.deepEqual(originalKeys, duplicateKeys)
1258
+ })
1259
+
1260
+ test('handles foreign domains correctly', ({ assert }) => {
1261
+ const fd = new DataDomain()
1262
+ const fd1 = fd.addModel()
1263
+ const fe1 = fd1.addEntity()
1264
+ fd.info.version = '1.0.0'
1265
+
1266
+ const dataDomain = new DataDomain()
1267
+ const model = dataDomain.addModel()
1268
+ const entity = model.addEntity()
1269
+ dataDomain.registerForeignDomain(fd)
1270
+
1271
+ const association = entity.addAssociation({ key: fe1.key, domain: fd.key })
1272
+
1273
+ const duplicate = association.duplicate()
1274
+
1275
+ const originalTargets = [...association.listTargets()]
1276
+ const duplicateTargets = [...duplicate.listTargets()]
1277
+
1278
+ assert.equal(originalTargets.length, 1)
1279
+ assert.equal(duplicateTargets.length, 1)
1280
+
1281
+ // Verify all targets are copied correctly
1282
+ const originalKeys = originalTargets.map((t) => t.key).sort()
1283
+ const duplicateKeys = duplicateTargets.map((t) => t.key).sort()
1284
+ assert.deepEqual(originalKeys, duplicateKeys)
1285
+ })
1286
+ })
@@ -729,3 +729,150 @@ test.group('DomainEntity.isChildOf()', () => {
729
729
  assert.isTrue(entity.isChildOf(dataDomain.key))
730
730
  })
731
731
  })
732
+
733
+ test.group('DomainEntity.generateUniqueName()', () => {
734
+ test('generates unique name when no fields exist', ({ assert }) => {
735
+ const dataDomain = new DataDomain()
736
+ const model = dataDomain.addModel()
737
+ const entity = model.addEntity()
738
+
739
+ const uniqueName = entity.generateUniqueName('test')
740
+ assert.equal(uniqueName, 'test_copy')
741
+ })
742
+
743
+ test('generates unique name when base name does not conflict', ({ assert }) => {
744
+ const dataDomain = new DataDomain()
745
+ const model = dataDomain.addModel()
746
+ const entity = model.addEntity()
747
+
748
+ // Add a property with a different name
749
+ entity.addProperty({ info: { name: 'existing_field' } })
750
+
751
+ const uniqueName = entity.generateUniqueName('test')
752
+ assert.equal(uniqueName, 'test_copy')
753
+ })
754
+
755
+ test('generates unique name when base_copy conflicts with existing property', ({ assert }) => {
756
+ const dataDomain = new DataDomain()
757
+ const model = dataDomain.addModel()
758
+ const entity = model.addEntity()
759
+
760
+ // Add a property that conflicts with the first generated name
761
+ entity.addProperty({ info: { name: 'test_copy' } })
762
+
763
+ const uniqueName = entity.generateUniqueName('test')
764
+ assert.equal(uniqueName, 'test_copy_2')
765
+ })
766
+
767
+ test('generates unique name when multiple conflicts exist with properties', ({ assert }) => {
768
+ const dataDomain = new DataDomain()
769
+ const model = dataDomain.addModel()
770
+ const entity = model.addEntity()
771
+
772
+ // Add properties that conflict with multiple generated names
773
+ entity.addProperty({ info: { name: 'test_copy' } })
774
+ entity.addProperty({ info: { name: 'test_copy_2' } })
775
+ entity.addProperty({ info: { name: 'test_copy_3' } })
776
+
777
+ const uniqueName = entity.generateUniqueName('test')
778
+ assert.equal(uniqueName, 'test_copy_4')
779
+ })
780
+
781
+ test('generates unique name when conflicts exist with associations', ({ assert }) => {
782
+ const dataDomain = new DataDomain()
783
+ const model = dataDomain.addModel()
784
+ const entity = model.addEntity()
785
+ const targetEntity = model.addEntity()
786
+
787
+ // Add an association that conflicts with the first generated name
788
+ const association = entity.addAssociation({ key: targetEntity.key })
789
+ association.info.name = 'test_copy'
790
+
791
+ const uniqueName = entity.generateUniqueName('test')
792
+ assert.equal(uniqueName, 'test_copy_2')
793
+ })
794
+
795
+ test('generates unique name when conflicts exist with both properties and associations', ({ assert }) => {
796
+ const dataDomain = new DataDomain()
797
+ const model = dataDomain.addModel()
798
+ const entity = model.addEntity()
799
+ const targetEntity = model.addEntity()
800
+
801
+ // Add property and association with conflicting names
802
+ entity.addProperty({ info: { name: 'test_copy' } })
803
+ const association = entity.addAssociation({ key: targetEntity.key })
804
+ association.info.name = 'test_copy_2'
805
+
806
+ const uniqueName = entity.generateUniqueName('test')
807
+ assert.equal(uniqueName, 'test_copy_3')
808
+ })
809
+
810
+ test('handles fields with undefined names', ({ assert }) => {
811
+ const dataDomain = new DataDomain()
812
+ const model = dataDomain.addModel()
813
+ const entity = model.addEntity()
814
+ const targetEntity = model.addEntity()
815
+
816
+ // Add property without a name (undefined name)
817
+ entity.addProperty({ info: {} })
818
+ // Add association without a name
819
+ entity.addAssociation({ key: targetEntity.key })
820
+
821
+ const uniqueName = entity.generateUniqueName('test')
822
+ assert.equal(uniqueName, 'test_copy')
823
+ })
824
+
825
+ test('generates incrementing counter correctly', ({ assert }) => {
826
+ const dataDomain = new DataDomain()
827
+ const model = dataDomain.addModel()
828
+ const entity = model.addEntity()
829
+
830
+ // Add many conflicting properties to test counter increment
831
+ for (let i = 0; i < 10; i++) {
832
+ const name = i === 0 ? 'test_copy' : `test_copy_${i + 1}`
833
+ entity.addProperty({ info: { name } })
834
+ }
835
+
836
+ const uniqueName = entity.generateUniqueName('test')
837
+ assert.equal(uniqueName, 'test_copy_11')
838
+ })
839
+
840
+ test('works with different base names', ({ assert }) => {
841
+ const dataDomain = new DataDomain()
842
+ const model = dataDomain.addModel()
843
+ const entity = model.addEntity()
844
+
845
+ entity.addProperty({ info: { name: 'user_copy' } })
846
+ entity.addProperty({ info: { name: 'product_copy' } })
847
+
848
+ const userUnique = entity.generateUniqueName('user')
849
+ const productUnique = entity.generateUniqueName('product')
850
+ const orderUnique = entity.generateUniqueName('order')
851
+
852
+ assert.equal(userUnique, 'user_copy_2')
853
+ assert.equal(productUnique, 'product_copy_2')
854
+ assert.equal(orderUnique, 'order_copy')
855
+ })
856
+
857
+ test('handles empty base name', ({ assert }) => {
858
+ const dataDomain = new DataDomain()
859
+ const model = dataDomain.addModel()
860
+ const entity = model.addEntity()
861
+
862
+ const uniqueName = entity.generateUniqueName('')
863
+ assert.equal(uniqueName, '_copy')
864
+ })
865
+
866
+ test('case sensitive name comparison', ({ assert }) => {
867
+ const dataDomain = new DataDomain()
868
+ const model = dataDomain.addModel()
869
+ const entity = model.addEntity()
870
+
871
+ // Add property with uppercase name
872
+ entity.addProperty({ info: { name: 'TEST_COPY' } })
873
+
874
+ // Should not conflict because comparison is case-sensitive
875
+ const uniqueName = entity.generateUniqueName('test')
876
+ assert.equal(uniqueName, 'test_copy')
877
+ })
878
+ })