package app import ( "context" "encoding/json" "errors" "os" "path/filepath" "strings" "testing" "time" "evening_detective_server/internal/modules/cudy" "evening_detective_server/internal/modules/processor_jwt" "evening_detective_server/internal/services/device_presence_service" "evening_detective_server/internal/services/game_service" "evening_detective_server/internal/services/screen_service" "evening_detective_server/internal/services/team_service" "evening_detective_server/internal/services/wifi_service" proto "evening_detective_server/proto" "github.com/google/uuid" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/encoding/protojson" ) func TestDeviceTransport(t *testing.T) { s := &server{devicePresenceService: device_presence_service.NewDevicePresenceService(true, nil, nil, nil)} ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-real-ip", "192.0.2.3", "grpcgateway-user-agent", "test-browser")) req := &proto.DeviceHeartbeatReq{DeviceID: uuid.NewString(), ConnectionID: uuid.NewString()} rsp, err := s.DeviceHeartbeat(ctx, req) if err != nil || !rsp.GetEnabled() || rsp.ServerTime == nil { t.Fatal(rsp, err) } for _, role := range []string{"organizer", "admin", "developer", "author", "user", ""} { t.Run(role, func(t *testing.T) { roleCtx := processor_jwt.ContextWithClaims(ctx, &processor_jwt.JWTClaims{Roles: []string{role}}) devices, err := s.GetDevices(roleCtx, &proto.GetDevicesReq{}) if role == "author" || role == "user" || role == "" { if status.Code(err) != codes.PermissionDenied { t.Fatal(err) } return } if err != nil || len(devices.Devices) != 1 { t.Fatal(devices, err) } connection := devices.Devices[0].Connections[0] if connection.IP != "192.0.2.3" || connection.UserAgent != "test-browser" { t.Fatal(connection) } raw, err := protojson.Marshal(devices) if err != nil { t.Fatal(err) } for _, secret := range []string{"password", "accessToken", "ScreenTokenHash", "email", "URL"} { if strings.Contains(string(raw), secret) { t.Fatal("secret field in response") } } }) } req.DeviceID = "bad" if _, err = s.DeviceHeartbeat(ctx, req); status.Code(err) != codes.InvalidArgument { t.Fatal(err) } s.devicePresenceService = device_presence_service.NewDevicePresenceService(false, nil, nil, nil) rsp, err = s.DeviceHeartbeat(ctx, req) if err != nil || rsp.GetEnabled() { t.Fatal(rsp, err) } raw, err := protojson.Marshal(rsp) if err != nil || !strings.Contains(string(raw), `"enabled":false`) { t.Fatal("disabled flag must be explicit", string(raw), err) } if _, err = s.GetDevices(ctx, &proto.GetDevicesReq{}); status.Code(err) != codes.FailedPrecondition { t.Fatal(err) } } func TestDeviceErrors(t *testing.T) { for _, test := range []struct { err error code codes.Code }{ {context.Canceled, codes.Canceled}, {context.DeadlineExceeded, codes.DeadlineExceeded}, {device_presence_service.ErrInvalid, codes.InvalidArgument}, {device_presence_service.ErrDisabled, codes.FailedPrecondition}, {device_presence_service.ErrPermission, codes.PermissionDenied}, {device_presence_service.ErrUnauthorized, codes.Unauthenticated}, {device_presence_service.ErrFull, codes.ResourceExhausted}, {team_service.ErrTeamNotFound, codes.NotFound}, {game_service.ErrGameNotFound, codes.NotFound}, {screen_service.ErrScreenNotFound, codes.Unauthenticated}, {errors.New("SQL password=secret /private/path"), codes.Internal}, } { err := deviceErrorStatus(test.err) if status.Code(err) != test.code || strings.Contains(err.Error(), "secret") || strings.Contains(err.Error(), "/private") { t.Fatal(err) } } } func TestWiFiUsesExistingPresencePermissions(t *testing.T) { path := filepath.Join(t.TempDir(), "state.json") signal := -65 snapshot := cudy.Snapshot{Enabled: true, CollectedAt: time.Now().UTC(), Clients: []cudy.Client{{IP: "192.168.10.9", MAC: "AA:BB:CC:DD:EE:01", Band: "5 GHz", Signal: &signal}}} data, err := json.Marshal(snapshot) if err != nil { t.Fatal(err) } if err := os.WriteFile(path, data, 0600); err != nil { t.Fatal(err) } s := &server{devicePresenceService: device_presence_service.NewDevicePresenceService(true, nil, nil, nil), wifiService: wifi_service.New(path)} for _, role := range []string{"organizer", "developer", "admin"} { ctx := processor_jwt.ContextWithClaims(context.Background(), &processor_jwt.JWTClaims{Roles: []string{role}}) result, err := s.GetDevices(ctx, &proto.GetDevicesReq{}) if err != nil || !result.Wifi.Enabled || result.Wifi.Stale || len(result.Wifi.Clients) != 1 || result.Wifi.Clients[0].GetSignal() != -65 { t.Fatalf("Wi-Fi for %s: %v, %v", role, result, err) } } if _, err := s.GetDevices(context.Background(), &proto.GetDevicesReq{}); status.Code(err) != codes.PermissionDenied { t.Fatal(err) } s.devicePresenceService = device_presence_service.NewDevicePresenceService(false, nil, nil, nil) ctx := processor_jwt.ContextWithClaims(context.Background(), &processor_jwt.JWTClaims{Roles: []string{"developer"}}) if _, err := s.GetDevices(ctx, &proto.GetDevicesReq{}); status.Code(err) != codes.FailedPrecondition { t.Fatal("prod exposes Wi-Fi", err) } }