@@ -3,6 +3,7 @@ package network
33import (
44 "context"
55 "fmt"
6+ "strings"
67 "testing"
78 "time"
89
@@ -693,3 +694,356 @@ type mockHTTPClientFunc struct {
693694func (m * mockHTTPClientFunc ) CheckEndpoint (ctx context.Context , endpoint EndpointConfig ) (* EndpointResult , error ) {
694695 return m .checkFunc (ctx , endpoint )
695696}
697+
698+ // TestParseEndpointConfig_EdgeCases tests edge cases in endpoint configuration parsing.
699+ func TestParseEndpointConfig_EdgeCases (t * testing.T ) {
700+ tests := []struct {
701+ name string
702+ config map [string ]interface {}
703+ wantErr bool
704+ errMsg string
705+ }{
706+ {
707+ name : "url is not a string" ,
708+ config : map [string ]interface {}{
709+ "url" : 12345 ,
710+ },
711+ wantErr : true ,
712+ errMsg : "url must be a string" ,
713+ },
714+ {
715+ name : "name is not a string" ,
716+ config : map [string ]interface {}{
717+ "url" : "https://example.com" ,
718+ "name" : 12345 ,
719+ },
720+ wantErr : true ,
721+ errMsg : "name must be a string" ,
722+ },
723+ {
724+ name : "method is not a string" ,
725+ config : map [string ]interface {}{
726+ "url" : "https://example.com" ,
727+ "method" : 12345 ,
728+ },
729+ wantErr : true ,
730+ errMsg : "method must be a string" ,
731+ },
732+ {
733+ name : "invalid method" ,
734+ config : map [string ]interface {}{
735+ "url" : "https://example.com" ,
736+ "method" : "POST" ,
737+ },
738+ wantErr : true ,
739+ errMsg : "invalid HTTP method" ,
740+ },
741+ {
742+ name : "expectedStatusCode is not a number" ,
743+ config : map [string ]interface {}{
744+ "url" : "https://example.com" ,
745+ "expectedStatusCode" : "200" ,
746+ },
747+ wantErr : true ,
748+ errMsg : "expectedStatusCode must be an integer" ,
749+ },
750+ {
751+ name : "expectedStatusCode as float64" ,
752+ config : map [string ]interface {}{
753+ "url" : "https://example.com" ,
754+ "expectedStatusCode" : float64 (201 ),
755+ },
756+ wantErr : false ,
757+ },
758+ {
759+ name : "followRedirects is not a bool" ,
760+ config : map [string ]interface {}{
761+ "url" : "https://example.com" ,
762+ "followRedirects" : "true" ,
763+ },
764+ wantErr : true ,
765+ errMsg : "followRedirects must be a boolean" ,
766+ },
767+ {
768+ name : "valid minimal config" ,
769+ config : map [string ]interface {}{
770+ "url" : "https://example.com" ,
771+ },
772+ wantErr : false ,
773+ },
774+ }
775+
776+ for _ , tt := range tests {
777+ t .Run (tt .name , func (t * testing.T ) {
778+ _ , err := parseEndpointConfig (tt .config )
779+
780+ if tt .wantErr {
781+ if err == nil {
782+ t .Error ("expected error, got nil" )
783+ return
784+ }
785+ if tt .errMsg != "" && ! strings .Contains (err .Error (), tt .errMsg ) {
786+ t .Errorf ("error = %v, want error containing %q" , err , tt .errMsg )
787+ }
788+ return
789+ }
790+
791+ if err != nil {
792+ t .Errorf ("unexpected error: %v" , err )
793+ }
794+ })
795+ }
796+ }
797+
798+ // TestNewConnectivityMonitor_EdgeCases tests edge cases in monitor creation.
799+ func TestNewConnectivityMonitor_EdgeCases (t * testing.T ) {
800+ tests := []struct {
801+ name string
802+ config types.MonitorConfig
803+ wantErr bool
804+ errMsg string
805+ }{
806+ {
807+ name : "too many endpoints" ,
808+ config : types.MonitorConfig {
809+ Name : "test-monitor" ,
810+ Type : "network-connectivity-check" ,
811+ Interval : 30 * time .Second ,
812+ Timeout : 10 * time .Second ,
813+ Enabled : true ,
814+ Config : map [string ]interface {}{
815+ "endpoints" : func () []interface {} {
816+ endpoints := make ([]interface {}, 51 ) // exceeds maxEndpoints (50)
817+ for i := 0 ; i < 51 ; i ++ {
818+ endpoints [i ] = map [string ]interface {}{
819+ "url" : fmt .Sprintf ("https://example%d.com" , i ),
820+ }
821+ }
822+ return endpoints
823+ }(),
824+ },
825+ },
826+ wantErr : true ,
827+ errMsg : "too many endpoints" ,
828+ },
829+ }
830+
831+ for _ , tt := range tests {
832+ t .Run (tt .name , func (t * testing.T ) {
833+ ctx := context .Background ()
834+ _ , err := NewConnectivityMonitor (ctx , tt .config )
835+
836+ if tt .wantErr {
837+ if err == nil {
838+ t .Error ("expected error, got nil" )
839+ return
840+ }
841+ if tt .errMsg != "" && ! strings .Contains (err .Error (), tt .errMsg ) {
842+ t .Errorf ("error = %v, want error containing %q" , err , tt .errMsg )
843+ }
844+ return
845+ }
846+
847+ if err != nil {
848+ t .Errorf ("unexpected error: %v" , err )
849+ }
850+ })
851+ }
852+ }
853+
854+ // TestIsValidHTTPMethod tests HTTP method validation.
855+ func TestIsValidHTTPMethod (t * testing.T ) {
856+ tests := []struct {
857+ method string
858+ valid bool
859+ }{
860+ {"GET" , true },
861+ {"HEAD" , true },
862+ {"OPTIONS" , true },
863+ {"get" , true }, // lowercase
864+ {"head" , true }, // lowercase
865+ {"options" , true }, // lowercase
866+ {"Get" , true }, // mixed case
867+ {"POST" , false },
868+ {"PUT" , false },
869+ {"DELETE" , false },
870+ {"PATCH" , false },
871+ {"" , false },
872+ {"INVALID" , false },
873+ }
874+
875+ for _ , tt := range tests {
876+ t .Run (tt .method , func (t * testing.T ) {
877+ got := isValidHTTPMethod (tt .method )
878+ if got != tt .valid {
879+ t .Errorf ("isValidHTTPMethod(%q) = %v, want %v" , tt .method , got , tt .valid )
880+ }
881+ })
882+ }
883+ }
884+
885+ // TestValidateURL tests URL validation for connectivity endpoints.
886+ func TestValidateURL (t * testing.T ) {
887+ tests := []struct {
888+ name string
889+ url string
890+ wantErr bool
891+ errMsg string
892+ }{
893+ {
894+ name : "valid https URL" ,
895+ url : "https://example.com" ,
896+ wantErr : false ,
897+ },
898+ {
899+ name : "valid http URL" ,
900+ url : "http://example.com" ,
901+ wantErr : false ,
902+ },
903+ {
904+ name : "valid URL with path" ,
905+ url : "https://example.com/api/v1/health" ,
906+ wantErr : false ,
907+ },
908+ {
909+ name : "valid URL with port" ,
910+ url : "https://example.com:8443" ,
911+ wantErr : false ,
912+ },
913+ {
914+ name : "missing scheme" ,
915+ url : "example.com" ,
916+ wantErr : true ,
917+ errMsg : "scheme" ,
918+ },
919+ {
920+ name : "unsupported scheme - ftp" ,
921+ url : "ftp://example.com" ,
922+ wantErr : true ,
923+ errMsg : "unsupported URL scheme" ,
924+ },
925+ {
926+ name : "unsupported scheme - file" ,
927+ url : "file:///etc/passwd" ,
928+ wantErr : true ,
929+ errMsg : "unsupported URL scheme" ,
930+ },
931+ {
932+ name : "missing host" ,
933+ url : "https://" ,
934+ wantErr : true ,
935+ errMsg : "host" ,
936+ },
937+ {
938+ name : "empty string" ,
939+ url : "" ,
940+ wantErr : true ,
941+ errMsg : "scheme" ,
942+ },
943+ }
944+
945+ for _ , tt := range tests {
946+ t .Run (tt .name , func (t * testing.T ) {
947+ err := validateURL (tt .url )
948+
949+ if tt .wantErr {
950+ if err == nil {
951+ t .Error ("expected error, got nil" )
952+ return
953+ }
954+ if tt .errMsg != "" && ! strings .Contains (err .Error (), tt .errMsg ) {
955+ t .Errorf ("error = %v, want error containing %q" , err , tt .errMsg )
956+ }
957+ return
958+ }
959+
960+ if err != nil {
961+ t .Errorf ("unexpected error: %v" , err )
962+ }
963+ })
964+ }
965+ }
966+
967+ // TestParseConnectivityConfig_EdgeCases tests edge cases in connectivity config parsing.
968+ func TestParseConnectivityConfig_EdgeCases (t * testing.T ) {
969+ tests := []struct {
970+ name string
971+ config map [string ]interface {}
972+ wantErr bool
973+ errMsg string
974+ }{
975+ {
976+ name : "nil config" ,
977+ config : nil ,
978+ wantErr : true ,
979+ errMsg : "connectivity monitor requires configuration" ,
980+ },
981+ {
982+ name : "failureThreshold invalid type" ,
983+ config : map [string ]interface {}{
984+ "failureThreshold" : "not-a-number" ,
985+ "endpoints" : []interface {}{
986+ map [string ]interface {}{"url" : "https://example.com" },
987+ },
988+ },
989+ wantErr : true ,
990+ errMsg : "failureThreshold must be an integer" ,
991+ },
992+ {
993+ name : "failureThreshold as float64" ,
994+ config : map [string ]interface {}{
995+ "failureThreshold" : float64 (3 ),
996+ "endpoints" : []interface {}{
997+ map [string ]interface {}{"url" : "https://example.com" },
998+ },
999+ },
1000+ wantErr : false ,
1001+ },
1002+ {
1003+ name : "failureThreshold too low" ,
1004+ config : map [string ]interface {}{
1005+ "failureThreshold" : 0 ,
1006+ "endpoints" : []interface {}{
1007+ map [string ]interface {}{"url" : "https://example.com" },
1008+ },
1009+ },
1010+ wantErr : true ,
1011+ errMsg : "failureThreshold must be at least 1" ,
1012+ },
1013+ {
1014+ name : "endpoints not a list" ,
1015+ config : map [string ]interface {}{
1016+ "endpoints" : "not-a-list" ,
1017+ },
1018+ wantErr : true ,
1019+ errMsg : "endpoints must be a list" ,
1020+ },
1021+ {
1022+ name : "endpoint not a map" ,
1023+ config : map [string ]interface {}{
1024+ "endpoints" : []interface {}{"not-a-map" },
1025+ },
1026+ wantErr : true ,
1027+ errMsg : "endpoint 0 must be a map" ,
1028+ },
1029+ }
1030+
1031+ for _ , tt := range tests {
1032+ t .Run (tt .name , func (t * testing.T ) {
1033+ _ , err := parseConnectivityConfig (tt .config )
1034+
1035+ if tt .wantErr {
1036+ if err == nil {
1037+ t .Error ("expected error but got none" )
1038+ } else if tt .errMsg != "" && ! strings .Contains (err .Error (), tt .errMsg ) {
1039+ t .Errorf ("error = %v, want error containing %q" , err , tt .errMsg )
1040+ }
1041+ return
1042+ }
1043+
1044+ if err != nil {
1045+ t .Errorf ("unexpected error: %v" , err )
1046+ }
1047+ })
1048+ }
1049+ }
0 commit comments