@@ -659,6 +659,7 @@ describe("replacePlaceholders", () => {
659659 expect ( result ) . toBe ( 'Config: {"stringKey": "hello", "numKey": 42}' ) ;
660660 } ) ;
661661} ) ;
662+
662663describe ( "replaceStringOnTopLevelKey" , ( ) => {
663664 it ( "replaces string in top-level key" , ( ) => {
664665 const input = {
@@ -916,7 +917,7 @@ describe("generateIdForThingDescription", () => {
916917} ) ;
917918
918919describe ( "prepareTdForSubmission" , ( ) => {
919- test ( "correctly processes TD with placeholders" , ( ) => {
920+ test ( "should correctly process TD with placeholders" , ( ) => {
920921 const td = {
921922 "@type" : "tm:ThingModel" ,
922923 "tm:required" : [ "prop1" ] ,
@@ -935,22 +936,14 @@ describe("prepareTdForSubmission", () => {
935936 const result = prepareTdForSubmission ( td as any , placeholderValues ) ;
936937
937938 // Check placeholders are replaced
939+ expect ( result [ "@type" ] ) . toBe ( "tm:ThingModel" ) ;
940+ expect ( result [ "tm:required" ] ) . toEqual ( [ "prop1" ] ) ;
938941 expect ( result . title ) . toBe ( "Test Thing" ) ;
939942 expect ( result . description ) . toBe ( "A temperature device" ) ;
940-
941- // Check TD fields are removed
942- expect ( result ) . not . toHaveProperty ( "@type" ) ;
943- expect ( result ) . not . toHaveProperty ( "tm:required" ) ;
944-
945- // Check ID is generated
946- expect ( result . id ) . toBeDefined ( ) ;
947- expect ( result . id ) . toMatch ( / ^ u r n : / ) ;
948-
949- // Check other properties are preserved
950943 expect ( result . properties ) . toEqual ( { prop1 : { type : "string" } } ) ;
951944 } ) ;
952945
953- test ( "processes TD without placeholders" , ( ) => {
946+ test ( "should return the same TD when there is no placeholders" , ( ) => {
954947 const td = {
955948 "@type" : "tm:ThingModel" ,
956949 "tm:required" : [ "prop1" ] ,
@@ -963,177 +956,39 @@ describe("prepareTdForSubmission", () => {
963956
964957 const result = prepareTdForSubmission ( td as any , { } ) ;
965958
966- // Check content is unchanged
967959 expect ( result . title ) . toBe ( "Static Title" ) ;
968960 expect ( result . description ) . toBe ( "A static description" ) ;
969-
970- // Check TD fields are removed
971- expect ( result ) . not . toHaveProperty ( "@type" ) ;
972- expect ( result ) . not . toHaveProperty ( "tm:required" ) ;
973-
974- // Check ID is generated
975- expect ( result . id ) . toBeDefined ( ) ;
976- } ) ;
977-
978- test ( "removes TD-specific fields" , ( ) => {
979- const td = {
980- "@type" : "tm:ThingModel" ,
981- "tm:required" : [ "prop1" , "prop2" ] ,
982- "tm:optional" : true ,
983- title : "Test Thing" ,
984- } ;
985-
986- const result = prepareTdForSubmission ( td as any , { } ) ;
987-
988- expect ( result ) . not . toHaveProperty ( "@type" ) ;
989- expect ( result ) . not . toHaveProperty ( "tm:required" ) ;
990- // Only @type and tm:required should be removed
991- expect ( result ) . toHaveProperty ( "tm:optional" , true ) ;
992- } ) ;
993-
994- test ( "replaces both string and numeric placeholders" , ( ) => {
995- const td = {
996- title : "Device {{id}}" ,
997- version : "{{version}}" ,
998- maxTemp : "{{maxTemp}}" ,
999- } ;
1000-
1001- const placeholderValues = {
1002- id : "ABC123" ,
1003- version : "2" ,
1004- maxTemp : "100.5" ,
1005- } ;
1006-
1007- const result = prepareTdForSubmission ( td as any , placeholderValues ) ;
1008-
1009- expect ( result . title ) . toBe ( "Device ABC123" ) ;
1010- expect ( result . version ) . toBe ( 2 ) ; // Number not string
1011- expect ( result . maxTemp ) . toBe ( 100.5 ) ; // Number not string
1012- } ) ;
1013-
1014- test ( "preserves existing TD structure" , ( ) => {
1015- const td = {
1016- "@type" : "tm:ThingModel" ,
1017- title : "{{title}}" ,
1018- properties : {
1019- temp : {
1020- type : "number" ,
1021- unit : "celsius" ,
1022- minimum : "{{min}}" ,
1023- maximum : "{{max}}" ,
1024- } ,
1025- } ,
1026- actions : {
1027- reset : { } ,
1028- } ,
1029- events : {
1030- overheat : { } ,
1031- } ,
1032- } ;
1033-
1034- const placeholderValues = {
1035- title : "Thermostat" ,
1036- min : "-20" ,
1037- max : "80" ,
1038- } ;
1039-
1040- const result = prepareTdForSubmission ( td as any , placeholderValues ) ;
1041-
1042- expect ( result . title ) . toBe ( "Thermostat" ) ;
1043- // @ts -ignore
1044- expect ( result . properties . temp . minimum ) . toBe ( - 20 ) ;
1045- // @ts -ignore
1046- expect ( result . properties . temp . maximum ) . toBe ( 80 ) ;
1047- // @ts -ignore
1048- expect ( result . properties . temp . unit ) . toBe ( "celsius" ) ;
1049- expect ( result . actions ) . toHaveProperty ( "reset" ) ;
1050- expect ( result . events ) . toHaveProperty ( "overheat" ) ;
1051- } ) ;
1052-
1053- test ( "generates a unique ID even when TD already has an ID" , ( ) => {
1054- const td = {
1055- id : "urn:existing:id" ,
1056- title : "Thing with ID" ,
1057- } ;
1058-
1059- const result = prepareTdForSubmission ( td as any , { } ) ;
1060-
1061- expect ( result . id ) . not . toBe ( "urn:existing:id" ) ;
1062- expect ( result . id ) . toMatch ( / ^ u r n : / ) ;
1063- } ) ;
1064-
1065- test ( "handles empty TD gracefully" , ( ) => {
1066- const td = { } ;
1067-
1068- const result = prepareTdForSubmission ( td as any , { } ) ;
1069-
1070- expect ( result ) . toHaveProperty ( "id" ) ;
1071- expect ( Object . keys ( result ) . length ) . toBe ( 1 ) ;
1072- } ) ;
1073-
1074- test ( "throws error with helpful message when processing fails" , ( ) => {
1075- const invalidTd = null as any ;
1076-
1077- expect ( ( ) => prepareTdForSubmission ( invalidTd , { } ) ) . toThrow (
1078- / E r r o r p r e p a r i n g T D f o r s u b m i s s i o n /
1079- ) ;
961+ expect ( result . properties ) . toEqual ( { prop1 : { type : "string" } } ) ;
962+ expect ( result [ "@type" ] ) . toBe ( "tm:ThingModel" ) ;
963+ expect ( result [ "tm:required" ] ) . toEqual ( [ "prop1" ] ) ;
1080964 } ) ;
1081965
1082- test ( "does not modify the original TD object" , ( ) => {
966+ test ( "should return a deep copy and not modify the original TD object" , ( ) => {
1083967 const original = {
1084968 "@type" : "tm:ThingModel" ,
1085969 "tm:required" : [ "prop1" ] ,
1086970 title : "Original Title" ,
1087971 } ;
972+ const result = prepareTdForSubmission ( original as any , { } ) ;
1088973
1089- const originalCopy = JSON . parse ( JSON . stringify ( original ) ) ;
1090-
1091- prepareTdForSubmission ( original as any , { } ) ;
1092-
1093- expect ( original ) . toEqual ( originalCopy ) ;
974+ expect ( result ) . toEqual ( original ) ;
975+ // Verify it's not the same reference (not same object)
976+ expect ( result ) . not . toBe ( original ) ;
977+ // Verify mutation of result doesn't affect original
978+ result . title = "Modified Title" ;
979+ expect ( original . title ) . toBe ( "Original Title" ) ;
1094980 } ) ;
1095-
1096- test ( "handles nested structures correctly" , ( ) => {
981+ test ( "should throw error when placeholder replacement creates invalid JSON" , ( ) => {
1097982 const td = {
1098- "@type" : "tm:ThingModel" ,
1099- title : "{{title}}" ,
1100- nested : {
1101- level1 : {
1102- level2 : {
1103- value : "{{nestedValue}}" ,
1104- } ,
1105- } ,
1106- } ,
1107- } ;
1108-
1109- const placeholderValues = {
1110- title : "Test Thing" ,
1111- nestedValue : "Deep Value" ,
1112- } ;
1113-
1114- const result = prepareTdForSubmission ( td as any , placeholderValues ) ;
1115-
1116- expect ( result . title ) . toBe ( "Test Thing" ) ;
1117- // @ts -ignore
1118- expect ( result . nested . level1 . level2 . value ) . toBe ( "Deep Value" ) ;
1119- } ) ;
1120-
1121- test ( "handles arrays correctly" , ( ) => {
1122- const td = {
1123- "@type" : "tm:ThingModel" ,
1124- title : "{{title}}" ,
1125- items : [ "{{item1}}" , "{{item2}}" , "static" ] ,
983+ title : "{{badValue}}" ,
1126984 } ;
1127985
1128986 const placeholderValues = {
1129- title : "Test Thing" ,
1130- item1 : "Dynamic 1" ,
1131- item2 : "Dynamic 2" ,
987+ badValue : '"}invalid' ,
1132988 } ;
1133989
1134- const result = prepareTdForSubmission ( td as any , placeholderValues ) ;
1135-
1136- expect ( result . title ) . toBe ( "Test Thing" ) ;
1137- expect ( result . items ) . toEqual ( [ "Dynamic 1" , "Dynamic 2" , "static" ] ) ;
990+ expect ( ( ) => prepareTdForSubmission ( td as any , placeholderValues ) ) . toThrow (
991+ / E r r o r p r e p a r i n g T D f o r s u b m i s s i o n /
992+ ) ;
1138993 } ) ;
1139994} ) ;
0 commit comments