package network_test import ( "context" "os" "testing" pbaero "github.com/flowy-live/llink/genproto/aero" mock_billing "github.com/flowy-live/llink/internal/billing/mocks" "github.com/flowy-live/llink/internal/human" mock_human "github.com/flowy-live/llink/internal/human/mocks" mock_livestore "github.com/flowy-live/llink/internal/livestore/mocks" "github.com/flowy-live/llink/internal/network" "github.com/flowy-live/llink/internal/testhelper" "github.com/flowy-live/llink/internal/testhelper/mocks/aero" "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) var dbPool *pgxpool.Pool func TestMain(m *testing.M) { dbPool = testhelper.SetupTestDB() defer testhelper.TeardownTestDB() ret := m.Run() os.Exit(ret) } func newTestService(t *testing.T) network.Service { ctrl := gomock.NewController(t) mockAero := aero.NewMockPrimaryClient(ctrl) mockAero.EXPECT(). ShootEmail(gomock.Any(), gomock.Any()). Return(&pbaero.ShootEmailResponse{}, nil). AnyTimes() mockBilling := mock_billing.NewMockService(ctrl) mockBilling.EXPECT().SyncSeats(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockPub := mock_livestore.NewMockMembershipPublisher(ctrl) mockPub.EXPECT().Add(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockPub.EXPECT().Remove(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockHuman := mock_human.NewMockService(ctrl) mockHuman.EXPECT().GetByID(gomock.Any(), gomock.Any()).Return(&human.Human{ ID: "test_human", Email: "test@gmail.com", EmailPrefix: "test", EmailNotificationsEnabled: false, }, nil).AnyTimes() return network.NewService(dbPool, mockAero, mockBilling, mockPub, mockHuman) } func TestNetworkService(t *testing.T) { ctx := context.Background() svc := newTestService(t) adminHumanId := "human_admin123" member1HumanId := "human_member1abc" member2HumanId := "human_member2def" strangerHumanId := "human_stranger789" // Test Create createdNetwork, err := svc.Create(ctx, "Test Network", adminHumanId) assert.NoError(t, err) assert.NotEmpty(t, createdNetwork.ID) assert.Equal(t, "Test Network", createdNetwork.Name) assert.Equal(t, adminHumanId, createdNetwork.AdminHumanId) assert.NotZero(t, createdNetwork.CreatedAt) // Test GetByID foundNetwork, err := svc.GetByID(ctx, createdNetwork.ID) assert.NoError(t, err) assert.Equal(t, createdNetwork.ID, foundNetwork.ID) assert.Equal(t, createdNetwork.Name, foundNetwork.Name) assert.Equal(t, createdNetwork.AdminHumanId, foundNetwork.AdminHumanId) // Test GetByID with non-existent id _, err = svc.GetByID(ctx, "network_nonexistent") assert.Error(t, err) assert.ErrorIs(t, err, network.ErrNotFound) // Test SetName err = svc.SetName(ctx, createdNetwork.ID, "Updated Network Name") assert.NoError(t, err) // Verify name was updated updatedNetwork, err := svc.GetByID(ctx, createdNetwork.ID) assert.NoError(t, err) assert.Equal(t, "Updated Network Name", updatedNetwork.Name) // Test SetName with non-existent id err = svc.SetName(ctx, "network_nonexistent", "New Name") assert.Error(t, err) assert.ErrorIs(t, err, network.ErrNotFound) // Test AddMembers err = svc.AddMembers(ctx, createdNetwork.ID, []string{member1HumanId, member2HumanId}) assert.NoError(t, err) // Test ListForHuman - should find network for admin networks, err := svc.ListForHuman(ctx, adminHumanId) assert.NoError(t, err) assert.Len(t, networks, 1) assert.Equal(t, createdNetwork.ID, networks[0].ID) // Test ListForHuman - should find network for member networks, err = svc.ListForHuman(ctx, member1HumanId) assert.NoError(t, err) assert.Len(t, networks, 1) assert.Equal(t, createdNetwork.ID, networks[0].ID) // Test ListForHuman - should return empty for non-member networks, err = svc.ListForHuman(ctx, strangerHumanId) assert.NoError(t, err) assert.Len(t, networks, 0) // Test RemoveMember err = svc.RemoveMember(ctx, createdNetwork.ID, member1HumanId) assert.NoError(t, err) // Verify member was removed networks, err = svc.ListForHuman(ctx, member1HumanId) assert.NoError(t, err) assert.Len(t, networks, 0) // member2 should still have access networks, err = svc.ListForHuman(ctx, member2HumanId) assert.NoError(t, err) assert.Len(t, networks, 1) // Create another network and verify ListForHuman returns multiple network2, err := svc.Create(ctx, "Second Network", member2HumanId) assert.NoError(t, err) networks, err = svc.ListForHuman(ctx, member2HumanId) assert.NoError(t, err) assert.Len(t, networks, 2) // Verify both networks are returned networkIDs := []string{networks[0].ID, networks[1].ID} assert.Contains(t, networkIDs, createdNetwork.ID) assert.Contains(t, networkIDs, network2.ID) } func TestNetworkInvitations(t *testing.T) { ctx := context.Background() svc := newTestService(t) adminHumanId := "human_invtest_admin" inviteeEmail := "invitee@example.com" inviteeHumanId := "human_invitee123" // Create a network net, err := svc.Create(ctx, "Invitation Test Network", adminHumanId) assert.NoError(t, err) // Invite by email err = svc.InviteByEmail(ctx, net.ID, []string{inviteeEmail}) assert.NoError(t, err) // List invitations for email invitations, err := svc.ListInvitationsForEmail(ctx, inviteeEmail) assert.NoError(t, err) assert.Len(t, invitations, 1) assert.Equal(t, net.ID, invitations[0].NetworkID) assert.Equal(t, inviteeEmail, invitations[0].Email) // List invitations for network invitations, err = svc.ListInvitationsForNetwork(ctx, net.ID) assert.NoError(t, err) assert.Len(t, invitations, 1) // Duplicate invite is idempotent err = svc.InviteByEmail(ctx, net.ID, []string{inviteeEmail}) assert.NoError(t, err) invitations, err = svc.ListInvitationsForNetwork(ctx, net.ID) assert.NoError(t, err) assert.Len(t, invitations, 1) // Accept invitation err = svc.AcceptInvitation(ctx, net.ID, inviteeEmail, inviteeHumanId) assert.NoError(t, err) // Invitation should be removed invitations, err = svc.ListInvitationsForEmail(ctx, inviteeEmail) assert.NoError(t, err) assert.Len(t, invitations, 0) // Human should now be a member isMember, err := svc.IsMember(ctx, net.ID, inviteeHumanId) assert.NoError(t, err) assert.True(t, isMember) // Test revoke invitation revokeEmail := "revokee@example.com" err = svc.InviteByEmail(ctx, net.ID, []string{revokeEmail}) assert.NoError(t, err) err = svc.RevokeInvitation(ctx, net.ID, revokeEmail) assert.NoError(t, err) invitations, err = svc.ListInvitationsForEmail(ctx, revokeEmail) assert.NoError(t, err) assert.Len(t, invitations, 0) }