property_test.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. package icq_legacy
  2. import (
  3. "context"
  4. "log/slog"
  5. "strconv"
  6. "testing"
  7. "testing/quick"
  8. "github.com/mk6i/open-oscar-server/config"
  9. "github.com/mk6i/open-oscar-server/state"
  10. "github.com/mk6i/open-oscar-server/wire"
  11. "github.com/stretchr/testify/mock"
  12. )
  13. // TestProperty_UINScreenNameRoundTrip verifies Property 8: UIN↔ScreenName round trip.
  14. // For any valid UIN (1 ≤ UIN ≤ 999999999), converting to state.IdentScreenName
  15. // via strconv.FormatUint(uint64(uin), 10) and back via
  16. // strconv.ParseUint(screenName.String(), 10, 32) produces the original UIN.
  17. //
  18. // **Validates: Requirements 7.1, 7.2**
  19. func TestProperty_UINScreenNameRoundTrip(t *testing.T) {
  20. f := func(raw uint32) bool {
  21. // Clamp to valid UIN range [1, 999999999]
  22. uin := (raw % 999999999) + 1
  23. // Forward: UIN → IdentScreenName
  24. screenName := state.NewIdentScreenName(strconv.FormatUint(uint64(uin), 10))
  25. // Reverse: IdentScreenName → UIN
  26. parsed, err := strconv.ParseUint(screenName.String(), 10, 32)
  27. if err != nil {
  28. return false
  29. }
  30. return uint32(parsed) == uin
  31. }
  32. if err := quick.Check(f, nil); err != nil {
  33. t.Errorf("UIN↔ScreenName round trip property failed: %v", err)
  34. }
  35. }
  36. // TestProperty_ServiceBehavioralEquivalence verifies Property 9: Service behavioral equivalence.
  37. // For any valid AuthRequest (UIN 1-999999999, non-empty password), the service
  38. // returns a consistent AuthResult. Specifically, for a user that doesn't exist,
  39. // it always returns {Success: false, ErrorCode: 0x0002}.
  40. //
  41. // **Validates: Requirements 8.1, 8.2**
  42. func TestProperty_ServiceBehavioralEquivalence(t *testing.T) {
  43. f := func(raw uint32, passByte byte) bool {
  44. // Clamp to valid UIN range [1, 999999999]
  45. uin := (raw % 999999999) + 1
  46. // Generate a non-empty password from the random byte
  47. password := string([]byte{'a' + passByte%26})
  48. authSvc := newMockAuthService(t)
  49. authSvc.EXPECT().FLAPLogin(mock.Anything, mock.Anything, config.Endpoint{}).
  50. Return(wire.TLVRestBlock{
  51. TLVList: []wire.TLV{
  52. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
  53. },
  54. }, nil)
  55. svc := NewICQLegacyService(
  56. authSvc,
  57. newMockUserManager(t),
  58. newMockAccountManager(t),
  59. newMockSessionRetriever(t),
  60. newMockMessageRelayer(t),
  61. newMockBuddyBroadcaster(t),
  62. newMockOfflineMessageManager(t),
  63. newMockICQUserFinder(t),
  64. newMockICQUserUpdater(t),
  65. newMockFeedbagManager(t),
  66. newMockRelationshipFetcher(t),
  67. newMockBuddyListRegistry(t),
  68. newMockBuddyService(t),
  69. newMockICBMService(t),
  70. &LegacySessionManager{sessions: map[uint32]*LegacySession{}},
  71. slog.Default(),
  72. )
  73. result, err := svc.AuthenticateUser(context.Background(), AuthRequest{
  74. UIN: uin,
  75. Password: password,
  76. })
  77. if err != nil {
  78. return false
  79. }
  80. // For a non-existent user, the service must always return
  81. // Success=false with ErrorCode=0x0002 (user not found)
  82. return !result.Success && result.ErrorCode == 0x0002
  83. }
  84. if err := quick.Check(f, nil); err != nil {
  85. t.Errorf("Service behavioral equivalence property failed: %v", err)
  86. }
  87. }