package posture import ( "encoding/json" "net/netip" "testing" "github.com/stretchr/testify/assert" ) func TestChecks_MarshalJSON(t *testing.T) { tests := []struct { name string checks *Checks want []byte wantErr bool }{ { name: "Valid Posture Checks Marshal", checks: &Checks{ ID: "id1", Name: "name1", Description: "desc1", AccountID: "acc1", Checks: ChecksDefinition{ NBVersionCheck: &NBVersionCheck{ MinVersion: "1.0.0", }, }, }, want: []byte(` { "ID": "id1", "Name": "name1", "Description": "desc1", "Checks": { "NBVersionCheck": { "MinVersion": "1.0.0" } } } `), wantErr: false, }, { name: "Empty Posture Checks Marshal", checks: &Checks{ ID: "", Name: "", Description: "", AccountID: "", Checks: ChecksDefinition{ NBVersionCheck: &NBVersionCheck{}, }, }, want: []byte(` { "ID": "", "Name": "", "Description": "", "Checks": { "NBVersionCheck": { "MinVersion": "" } } } `), wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := json.Marshal(tt.checks) if (err != nil) != tt.wantErr { t.Errorf("Checks.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) return } assert.JSONEq(t, string(got), string(tt.want)) assert.Equal(t, tt.checks, tt.checks.Copy(), "original Checks should not be modified") }) } } func TestChecks_UnmarshalJSON(t *testing.T) { testCases := []struct { name string in []byte expected *Checks expectedError bool }{ { name: "Valid JSON Posture Checks Unmarshal", in: []byte(` { "ID": "id1", "Name": "name1", "Description": "desc1", "Checks": { "NBVersionCheck": { "MinVersion": "1.0.0" } } } `), expected: &Checks{ ID: "id1", Name: "name1", Description: "desc1", Checks: ChecksDefinition{ NBVersionCheck: &NBVersionCheck{ MinVersion: "1.0.0", }, }, }, expectedError: false, }, { name: "Invalid JSON Posture Checks Unmarshal", in: []byte(`{`), expectedError: true, }, { name: "Empty JSON Posture Check Unmarshal", in: []byte(`{}`), expected: &Checks{}, expectedError: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var checks Checks err := json.Unmarshal(tc.in, &checks) if tc.expectedError { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, &checks) } }) } } func TestChecks_Validate(t *testing.T) { testCases := []struct { name string checks Checks expectedError bool }{ { name: "Empty name", checks: Checks{}, expectedError: true, }, { name: "Empty checks", checks: Checks{ Name: "Default", Checks: ChecksDefinition{}, }, expectedError: true, }, { name: "Valid checks version", checks: Checks{ Name: "default", Checks: ChecksDefinition{ NBVersionCheck: &NBVersionCheck{ MinVersion: "0.25.0", }, OSVersionCheck: &OSVersionCheck{ Ios: &MinVersionCheck{ MinVersion: "13.0.1", }, Linux: &MinKernelVersionCheck{ MinKernelVersion: "5.3.3-dev", }, }, }, }, expectedError: false, }, { name: "Invalid checks version", checks: Checks{ Checks: ChecksDefinition{ NBVersionCheck: &NBVersionCheck{ MinVersion: "abc", }, OSVersionCheck: &OSVersionCheck{ Android: &MinVersionCheck{ MinVersion: "dev", }, }, }, }, expectedError: true, }, { name: "Combined valid and invalid checks version", checks: Checks{ Checks: ChecksDefinition{ NBVersionCheck: &NBVersionCheck{ MinVersion: "abc", }, OSVersionCheck: &OSVersionCheck{ Windows: &MinKernelVersionCheck{ MinKernelVersion: "10.0.1234", }, Darwin: &MinVersionCheck{ MinVersion: "13.0.1", }, }, }, }, expectedError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := tc.checks.Validate() if tc.expectedError { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestChecks_Copy(t *testing.T) { check := &Checks{ ID: "1", Name: "default", Description: "description", AccountID: "accountID", Checks: ChecksDefinition{ NBVersionCheck: &NBVersionCheck{ MinVersion: "0.25.0", }, OSVersionCheck: &OSVersionCheck{ Android: &MinVersionCheck{ MinVersion: "13", }, Darwin: &MinVersionCheck{ MinVersion: "14.2.0", }, Ios: &MinVersionCheck{ MinVersion: "17.3.0", }, Linux: &MinKernelVersionCheck{ MinKernelVersion: "6.5.11-linuxkit", }, Windows: &MinKernelVersionCheck{ MinKernelVersion: "10.0.14393", }, }, GeoLocationCheck: &GeoLocationCheck{ Locations: []Location{ { CountryCode: "DE", CityName: "Berlin", }, }, Action: CheckActionAllow, }, PeerNetworkRangeCheck: &PeerNetworkRangeCheck{ Ranges: []netip.Prefix{ netip.MustParsePrefix("192.168.0.0/24"), netip.MustParsePrefix("10.0.0.0/8"), }, Action: CheckActionDeny, }, ProcessCheck: &ProcessCheck{ Processes: []Process{ { MacPath: "/Applications/NetBird.app/Contents/MacOS/netbird", WindowsPath: "C:\\ProgramData\\NetBird\\netbird.exe", }, }, }, }, } checkCopy := check.Copy() assert.Equal(t, check.ID, checkCopy.ID) assert.Equal(t, check.Name, checkCopy.Name) assert.Equal(t, check.Description, checkCopy.Description) assert.Equal(t, check.AccountID, checkCopy.AccountID) assert.Equal(t, check.Checks.Copy(), checkCopy.Checks.Copy()) assert.ElementsMatch(t, check.GetChecks(), checkCopy.GetChecks()) // Updating the original check should not take effect on copy check.Name = "name" assert.NotSame(t, check, checkCopy) }