package organization import ( "context" "errors" "testing" "git.sendico.io/tech/sxchnge/bff/pkg/auth" "git.sendico.io/tech/sxchnge/bff/pkg/db/repository/builder" "git.sendico.io/tech/sxchnge/bff/pkg/db/storable" "git.sendico.io/tech/sxchnge/bff/pkg/db/template" "git.sendico.io/tech/sxchnge/bff/pkg/merrors" "git.sendico.io/tech/sxchnge/bff/pkg/model" "git.sendico.io/tech/sxchnge/bff/pkg/mservice" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) // membershipTestRepository shadows FindOneByFilter with a members-aware implementation. // When findErr is set, FindOneByFilter returns it immediately. type membershipTestRepository struct { *memoryOrganizationRepository findErr error } func (r *membershipTestRepository) FindOneByFilter(_ context.Context, query builder.Query, result storable.Storable) error { if r.findErr != nil { return r.findErr } r.mu.RLock() defer r.mu.RUnlock() for _, id := range r.order { if org, ok := r.data[id]; ok && memberMatchesQuery(query, org) { dst, ok := result.(*model.Organization) if !ok { return merrors.InvalidDataType("expected organization result") } *dst = *cloneOrganization(org) return nil } } return merrors.ErrNoData } func memberMatchesQuery(query builder.Query, org *model.Organization) bool { if query == nil { return true } for _, elem := range query.BuildQuery() { switch elem.Key { case storable.IDField: id, ok := elem.Value.(bson.ObjectID) if !ok || *org.GetID() != id { return false } case "members": cond, ok := elem.Value.(bson.M) if !ok { return false } ref, ok := cond[string(builder.Eq)].(bson.ObjectID) if !ok { return false } found := false for _, m := range org.Members { if m == ref { found = true break } } if !found { return false } } } return true } func newMemberTestDB(t *testing.T, findErr error) *OrganizationDB { t.Helper() repo := &membershipTestRepository{ memoryOrganizationRepository: newMemoryOrganizationRepository(), findErr: findErr, } dbImp := &template.DBImp[*model.Organization]{ Logger: zap.NewNop(), Repository: repo, } dbImp.SetDeleter(func(ctx context.Context, objectRef bson.ObjectID) error { return repo.Delete(ctx, objectRef) }) return &OrganizationDB{ ProtectedDBImp: auth.ProtectedDBImp[*model.Organization]{ DBImp: dbImp, Enforcer: allowAllEnforcer{}, PermissionRef: bson.NewObjectID(), Collection: mservice.Organizations, }, } } func TestOrganizationDB_IsMember_Member(t *testing.T) { ctx := context.Background() db := newMemberTestDB(t, nil) callerRef := bson.NewObjectID() org := &model.Organization{} org.SetID(bson.NewObjectID()) org.Members = []bson.ObjectID{callerRef} require.NoError(t, db.Create(ctx, bson.NewObjectID(), *org.GetID(), org)) ok, err := db.IsMember(ctx, *org.GetID(), callerRef) require.NoError(t, err) assert.True(t, ok) } func TestOrganizationDB_IsMember_NonMember(t *testing.T) { ctx := context.Background() db := newMemberTestDB(t, nil) callerRef := bson.NewObjectID() otherRef := bson.NewObjectID() org := &model.Organization{} org.SetID(bson.NewObjectID()) org.Members = []bson.ObjectID{otherRef} require.NoError(t, db.Create(ctx, bson.NewObjectID(), *org.GetID(), org)) ok, err := db.IsMember(ctx, *org.GetID(), callerRef) require.NoError(t, err) assert.False(t, ok) } func TestOrganizationDB_IsMember_OrgNotFound(t *testing.T) { ctx := context.Background() db := newMemberTestDB(t, nil) ok, err := db.IsMember(ctx, bson.NewObjectID(), bson.NewObjectID()) require.NoError(t, err) assert.False(t, ok) } func TestOrganizationDB_IsMember_DBError(t *testing.T) { ctx := context.Background() boom := merrors.Internal("backend down") db := newMemberTestDB(t, boom) ok, err := db.IsMember(ctx, bson.NewObjectID(), bson.NewObjectID()) assert.False(t, ok) assert.True(t, errors.Is(err, boom)) }