Skip to content

Commit

Permalink
ns: Implement MAC settings profile List endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
halimi committed Nov 21, 2024
1 parent 0a26f8d commit 5c16798
Show file tree
Hide file tree
Showing 6 changed files with 340 additions and 20 deletions.
16 changes: 14 additions & 2 deletions pkg/networkserver/grpc_mac_settings_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,25 @@ func (m *NsMACSettingsProfileRegistry) Delete(ctx context.Context, req *ttnpb.De
}

// List lists the MAC settings profiles.
func (*NsMACSettingsProfileRegistry) List(ctx context.Context, req *ttnpb.ListMACSettingsProfilesRequest,
func (m *NsMACSettingsProfileRegistry) List(ctx context.Context, req *ttnpb.ListMACSettingsProfilesRequest,
) (*ttnpb.ListMACSettingsProfilesResponse, error) {
if err := rights.RequireApplication(
ctx, req.ApplicationIds, ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ,
); err != nil {
return nil, err
}
paths := []string{"ids", "mac_settings"}
if req.FieldMask != nil {
paths = req.FieldMask.GetPaths()
}
profiles, err := m.registry.List(ctx, req.ApplicationIds, paths)
if err != nil {
logRegistryRPCError(ctx, err, "Failed to list MAC settings profiles")
return nil, err
}

return &ttnpb.ListMACSettingsProfilesResponse{}, nil
return &ttnpb.ListMACSettingsProfilesResponse{
MacSettingsProfiles: profiles,
TotalCount: uint32(len(profiles)), // nolint: gosec
}, nil
}
228 changes: 228 additions & 0 deletions pkg/networkserver/grpc_mac_settings_profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -944,3 +944,231 @@ func TestMACSettingsProfileRegistryDelete(t *testing.T) {
})
}
}

func TestMACSettingsProfileRegistryList(t *testing.T) {
t.Parallel()
nilProfileAssertion := func(t *testing.T, profile *ttnpb.ListMACSettingsProfilesResponse) bool {
t.Helper()
return assertions.New(t).So(profile, should.BeNil)
}
nilErrorAssertion := func(t *testing.T, err error) bool {
t.Helper()
return assertions.New(t).So(err, should.BeNil)
}
permissionDeniedErrorAssertion := func(t *testing.T, err error) bool {
t.Helper()
return assertions.New(t).So(errors.IsPermissionDenied(err), should.BeTrue)
}
notFoundErrorAssertion := func(t *testing.T, err error) bool {
t.Helper()
return assertions.New(t).So(errors.IsNotFound(err), should.BeTrue)
}

registeredProfileIDs := &ttnpb.MACSettingsProfileIdentifiers{
ApplicationIds: &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"},
ProfileId: "test-profile-id",
}

for _, tc := range []struct {
Name string
ContextFunc func(context.Context) context.Context
ListFunc func(context.Context, *ttnpb.ApplicationIdentifiers, []string) ([]*ttnpb.MACSettingsProfile, error) // nolint: lll
ProfileRequest *ttnpb.ListMACSettingsProfilesRequest
ProfileAssertion func(*testing.T, *ttnpb.ListMACSettingsProfilesResponse) bool
ErrorAssertion func(*testing.T, error) bool
ListCalls uint64
}{
{
Name: "Permission denied",
ContextFunc: func(ctx context.Context) context.Context {
return rights.NewContext(ctx, &rights.Rights{
ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{
unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}): nil,
}),
})
},
ListFunc: func(
ctx context.Context,
_ *ttnpb.ApplicationIdentifiers,
_ []string,
) ([]*ttnpb.MACSettingsProfile, error) {
err := errors.New("ListFunc must not be called")
test.MustTFromContext(ctx).Error(err)
return nil, err
},
ProfileRequest: &ttnpb.ListMACSettingsProfilesRequest{
ApplicationIds: registeredProfileIDs.ApplicationIds,
FieldMask: ttnpb.FieldMask("mac_settings"),
},
ProfileAssertion: nilProfileAssertion,
ErrorAssertion: permissionDeniedErrorAssertion,
ListCalls: 0,
},
{
Name: "Invalid application ID",
ContextFunc: func(ctx context.Context) context.Context {
return rights.NewContext(ctx, &rights.Rights{
ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{
unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{
ApplicationId: "invalid-application",
}): ttnpb.RightsFrom(
ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ,
),
}),
})
},
ListFunc: func(
ctx context.Context,
_ *ttnpb.ApplicationIdentifiers,
_ []string,
) ([]*ttnpb.MACSettingsProfile, error) {
err := errors.New("ListFunc must not be called")
test.MustTFromContext(ctx).Error(err)
return nil, err
},
ProfileRequest: &ttnpb.ListMACSettingsProfilesRequest{
ApplicationIds: registeredProfileIDs.ApplicationIds,
FieldMask: ttnpb.FieldMask("mac_settings"),
},
ProfileAssertion: nilProfileAssertion,
ErrorAssertion: permissionDeniedErrorAssertion,
ListCalls: 0,
},
{
Name: "Not found",
ContextFunc: func(ctx context.Context) context.Context {
return rights.NewContext(ctx, &rights.Rights{
ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{
unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{
ApplicationId: "test-app-id",
}): ttnpb.RightsFrom(
ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ,
),
}),
})
},
ListFunc: func(
ctx context.Context,
ids *ttnpb.ApplicationIdentifiers,
paths []string,
) ([]*ttnpb.MACSettingsProfile, error) {
a := assertions.New(test.MustTFromContext(ctx))
a.So(ids, should.Resemble, ids)
a.So(paths, should.HaveSameElementsDeep, []string{
"mac_settings",
})
return nil, errNotFound.New()
},
ProfileRequest: &ttnpb.ListMACSettingsProfilesRequest{
ApplicationIds: registeredProfileIDs.ApplicationIds,
FieldMask: ttnpb.FieldMask("mac_settings"),
},
ProfileAssertion: nilProfileAssertion,
ErrorAssertion: notFoundErrorAssertion,
ListCalls: 1,
},
{
Name: "Found",
ContextFunc: func(ctx context.Context) context.Context {
return rights.NewContext(ctx, &rights.Rights{
ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{
unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{
ApplicationId: "test-app-id",
}): ttnpb.RightsFrom(
ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ,
),
}),
})
},
ListFunc: func(
ctx context.Context,
ids *ttnpb.ApplicationIdentifiers,
paths []string,
) ([]*ttnpb.MACSettingsProfile, error) {
a := assertions.New(test.MustTFromContext(ctx))
a.So(ids, should.Resemble, ids)
a.So(paths, should.HaveSameElementsDeep, []string{
"ids",
"mac_settings",
})
return []*ttnpb.MACSettingsProfile{ttnpb.Clone(&ttnpb.MACSettingsProfile{
Ids: registeredProfileIDs,
MacSettings: &ttnpb.MACSettings{
ResetsFCnt: &ttnpb.BoolValue{Value: true},
},
})}, nil
},
ProfileRequest: &ttnpb.ListMACSettingsProfilesRequest{
ApplicationIds: registeredProfileIDs.ApplicationIds,
FieldMask: ttnpb.FieldMask("ids", "mac_settings"),
},
ProfileAssertion: func(t *testing.T, profile *ttnpb.ListMACSettingsProfilesResponse) bool {
t.Helper()
a := assertions.New(t)
a.So(profile, should.NotBeNil)
a.So(profile.MacSettingsProfiles, should.HaveLength, 1)
a.So(profile.TotalCount, should.Equal, 1)
return a.So(profile.MacSettingsProfiles, should.Resemble, []*ttnpb.MACSettingsProfile{{
Ids: &ttnpb.MACSettingsProfileIdentifiers{
ApplicationIds: &ttnpb.ApplicationIdentifiers{
ApplicationId: "test-app-id",
},
ProfileId: "test-profile-id",
},
MacSettings: &ttnpb.MACSettings{
ResetsFCnt: &ttnpb.BoolValue{Value: true},
},
}})
},
ErrorAssertion: nilErrorAssertion,
ListCalls: 1,
},
} {
tc := tc
test.RunSubtest(t, test.SubtestConfig{
Name: tc.Name,
Parallel: true,
Func: func(ctx context.Context, t *testing.T, a *assertions.Assertion) {
t.Helper()
var listCalls uint64

ns, ctx, _, stop := StartTest(
ctx,
TestConfig{
NetworkServer: Config{
MACSettingsProfileRegistry: &MockMACSettingsProfileRegistry{
ListFunc: func(
ctx context.Context,
ids *ttnpb.ApplicationIdentifiers,
paths []string,
) ([]*ttnpb.MACSettingsProfile, error) {
atomic.AddUint64(&listCalls, 1)
return tc.ListFunc(ctx, ids, paths)
},
},
},
TaskStarter: StartTaskExclude(
DownlinkProcessTaskName,
DownlinkDispatchTaskName,
),
},
)
defer stop()

ns.AddContextFiller(tc.ContextFunc)
ns.AddContextFiller(func(ctx context.Context) context.Context {
return test.ContextWithTB(ctx, t)
})

req := ttnpb.Clone(tc.ProfileRequest)

profile, err := ttnpb.NewNsMACSettingsProfileRegistryClient(ns.LoopbackConn()).List(ctx, req)
if a.So(tc.ErrorAssertion(t, err), should.BeTrue) {
a.So(tc.ProfileAssertion(t, profile), should.BeTrue)
}
a.So(req, should.Resemble, tc.ProfileRequest)
a.So(listCalls, should.Equal, tc.ListCalls)
},
})
}
}
16 changes: 16 additions & 0 deletions pkg/networkserver/networkserver_util_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2609,6 +2609,11 @@ type MockMACSettingsProfileRegistry struct {
paths []string,
f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error),
) (*ttnpb.MACSettingsProfile, error)
ListFunc func(
ctx context.Context,
ids *ttnpb.ApplicationIdentifiers,
paths []string,
) ([]*ttnpb.MACSettingsProfile, error)
}

func (m MockMACSettingsProfileRegistry) Get(
Expand All @@ -2633,3 +2638,14 @@ func (m MockMACSettingsProfileRegistry) Set(
}
return m.SetFunc(ctx, ids, paths, f)
}

func (m MockMACSettingsProfileRegistry) List(
ctx context.Context,
ids *ttnpb.ApplicationIdentifiers,
paths []string,
) ([]*ttnpb.MACSettingsProfile, error) {
if m.ListFunc == nil {
panic("ListFunc not set")
}
return m.ListFunc(ctx, ids, paths)
}
Loading

0 comments on commit 5c16798

Please sign in to comment.