@@ -11,6 +11,7 @@ import (
1111 "github.com/stretchr/testify/assert"
1212 "github.com/stretchr/testify/require"
1313
14+ "github.com/smartcontractkit/cre-cli/cmd/generate-bindings/bindings"
1415 "github.com/smartcontractkit/cre-cli/internal/runtime"
1516)
1617
@@ -442,6 +443,47 @@ func TestProcessAbiDirectory_NoAbiFiles(t *testing.T) {
442443 assert .Contains (t , err .Error (), "no .abi files found" )
443444}
444445
446+ func TestProcessAbiDirectory_PackageNameCollision (t * testing.T ) {
447+ tempDir , err := os .MkdirTemp ("" , "generate-bindings-test" )
448+ require .NoError (t , err )
449+ defer os .RemoveAll (tempDir )
450+
451+ abiDir := filepath .Join (tempDir , "abi" )
452+ outDir := filepath .Join (tempDir , "generated" )
453+
454+ err = os .MkdirAll (abiDir , 0755 )
455+ require .NoError (t , err )
456+
457+ abiContent := `[{"type":"function","name":"test","inputs":[],"outputs":[]}]`
458+
459+ // "TestContract" -> "test_contract"
460+ // "test_contract" -> "test_contract"
461+ err = os .WriteFile (filepath .Join (abiDir , "TestContract.abi" ), []byte (abiContent ), 0600 )
462+ require .NoError (t , err )
463+ err = os .WriteFile (filepath .Join (abiDir , "test_contract.abi" ), []byte (abiContent ), 0600 )
464+ require .NoError (t , err )
465+
466+ logger := zerolog .New (os .Stderr ).With ().Timestamp ().Logger ()
467+ runtimeCtx := & runtime.Context {
468+ Logger : & logger ,
469+ }
470+ handler := newHandler (runtimeCtx )
471+
472+ inputs := Inputs {
473+ ProjectRoot : tempDir ,
474+ ChainFamily : "evm" ,
475+ Language : "go" ,
476+ AbiPath : abiDir ,
477+ PkgName : "bindings" ,
478+ OutPath : outDir ,
479+ }
480+
481+ err = handler .processAbiDirectory (inputs )
482+ fmt .Println (err .Error ())
483+ require .Error (t , err )
484+ require .Equal (t , err .Error (), "package name collision: multiple contracts would generate the same package name 'test_contract' (contracts are converted to snake_case for package names). Please rename one of your contract files to avoid this conflict" )
485+ }
486+
445487func TestProcessAbiDirectory_NonExistentDirectory (t * testing.T ) {
446488 logger := zerolog .New (os .Stderr ).With ().Timestamp ().Logger ()
447489 runtimeCtx := & runtime.Context {
@@ -463,3 +505,133 @@ func TestProcessAbiDirectory_NonExistentDirectory(t *testing.T) {
463505 // For non-existent directory, filepath.Glob returns empty slice, so we get the "no .abi files found" error
464506 assert .Contains (t , err .Error (), "no .abi files found" )
465507}
508+
509+ // TestGenerateBindings_UnconventionalNaming tests binding generation for contracts
510+ // with unconventional naming patterns to verify correct handling or appropriate errors.
511+ func TestGenerateBindings_UnconventionalNaming (t * testing.T ) {
512+ tests := []struct {
513+ name string
514+ contractABI string
515+ pkgName string
516+ typeName string
517+ shouldFail bool
518+ expectedErrMsg string
519+ }{
520+ {
521+ name : "DollarSignInStructField" ,
522+ pkgName : "dollarsign" ,
523+ typeName : "DollarContract" ,
524+ contractABI : `[
525+ {"type":"function","name":"getValue","inputs":[],"outputs":[{"name":"","type":"tuple","components":[{"name":"$name","type":"string"},{"name":"$value","type":"uint256"}]}],"stateMutability":"view"}
526+ ]` ,
527+ shouldFail : true ,
528+ expectedErrMsg : "invalid name" ,
529+ },
530+ {
531+ name : "DollarSignInFunctionName" ,
532+ pkgName : "dollarsign" ,
533+ typeName : "DollarFuncContract" ,
534+ contractABI : `[
535+ {"type":"function","name":"$getValue","inputs":[],"outputs":[{"name":"","type":"uint256"}],"stateMutability":"view"}
536+ ]` ,
537+ shouldFail : true ,
538+ expectedErrMsg : "illegal character" ,
539+ },
540+ {
541+ name : "DollarSignInEventName" ,
542+ pkgName : "dollarsign" ,
543+ typeName : "DollarEventContract" ,
544+ contractABI : `[
545+ {"type":"event","name":"$Transfer","inputs":[{"name":"from","type":"address","indexed":true}],"anonymous":false}
546+ ]` ,
547+ shouldFail : true ,
548+ expectedErrMsg : "illegal character" ,
549+ },
550+ {
551+ name : "camelCaseContractName" ,
552+ pkgName : "camelcase" ,
553+ typeName : "camelCaseContract" ,
554+ contractABI : `[
555+ {"type":"function","name":"getValue","inputs":[],"outputs":[{"name":"","type":"uint256"}],"stateMutability":"view"}
556+ ]` ,
557+ shouldFail : false ,
558+ },
559+ {
560+ name : "snake_case_contract_name" ,
561+ pkgName : "snakecase" ,
562+ typeName : "snake_case_contract" ,
563+ contractABI : `[
564+ {"type":"function","name":"get_value","inputs":[],"outputs":[{"name":"","type":"uint256"}],"stateMutability":"view"}
565+ ]` ,
566+ shouldFail : false ,
567+ },
568+ {
569+ name : "snake_case_function_names" ,
570+ pkgName : "snakefunc" ,
571+ typeName : "SnakeFuncContract" ,
572+ contractABI : `[
573+ {"type":"function","name":"get_user_balance","inputs":[{"name":"user_address","type":"address"}],"outputs":[{"name":"user_balance","type":"uint256"}],"stateMutability":"view"},
574+ {"type":"event","name":"balance_updated","inputs":[{"name":"user_address","type":"address","indexed":true},{"name":"new_balance","type":"uint256","indexed":false}],"anonymous":false}
575+ ]` ,
576+ shouldFail : false ,
577+ },
578+ {
579+ name : "ALLCAPS_contract_name" ,
580+ pkgName : "allcaps" ,
581+ typeName : "ALLCAPSCONTRACT" ,
582+ contractABI : `[
583+ {"type":"function","name":"GETVALUE","inputs":[],"outputs":[{"name":"","type":"uint256"}],"stateMutability":"view"}
584+ ]` ,
585+ shouldFail : false ,
586+ },
587+ {
588+ name : "MixedCase_With_Underscores" ,
589+ pkgName : "mixedcase" ,
590+ typeName : "Mixed_Case_Contract" ,
591+ contractABI : `[
592+ {"type":"function","name":"Get_User_Data","inputs":[{"name":"User_Id","type":"uint256"}],"outputs":[{"name":"","type":"string"}],"stateMutability":"view"}
593+ ]` ,
594+ shouldFail : false ,
595+ },
596+ {
597+ name : "NumericSuffix" ,
598+ pkgName : "numeric" ,
599+ typeName : "Contract123" ,
600+ contractABI : `[
601+ {"type":"function","name":"getValue1","inputs":[],"outputs":[{"name":"value1","type":"uint256"}],"stateMutability":"view"},
602+ {"type":"function","name":"getValue2","inputs":[],"outputs":[{"name":"value2","type":"uint256"}],"stateMutability":"view"}
603+ ]` ,
604+ shouldFail : false ,
605+ },
606+ }
607+
608+ for _ , tc := range tests {
609+ t .Run (tc .name , func (t * testing.T ) {
610+ tempDir , err := os .MkdirTemp ("" , "bindings-unconventional-test" )
611+ require .NoError (t , err )
612+ defer os .RemoveAll (tempDir )
613+
614+ abiFile := filepath .Join (tempDir , tc .typeName + ".abi" )
615+ err = os .WriteFile (abiFile , []byte (tc .contractABI ), 0600 )
616+ require .NoError (t , err )
617+
618+ outFile := filepath .Join (tempDir , "bindings.go" )
619+ err = bindings .GenerateBindings ("" , abiFile , tc .pkgName , tc .typeName , outFile )
620+
621+ if tc .shouldFail {
622+ require .Error (t , err , "Expected binding generation to fail for %s" , tc .name )
623+ if tc .expectedErrMsg != "" {
624+ assert .Contains (t , err .Error (), tc .expectedErrMsg , "Error message should contain expected text" )
625+ }
626+ } else {
627+ require .NoError (t , err , "Binding generation should succeed for %s" , tc .name )
628+
629+ content , err := os .ReadFile (outFile )
630+ require .NoError (t , err )
631+ assert .NotEmpty (t , content , "Generated bindings should not be empty" )
632+
633+ assert .Contains (t , string (content ), fmt .Sprintf ("package %s" , tc .pkgName ))
634+ }
635+ })
636+ }
637+ }
0 commit comments