@@ -43,8 +43,21 @@ func (r *fakeProfileRepo) GetProfile(_ context.Context, userID uuid.UUID) (*doma
4343 return & copied , nil
4444}
4545
46+ // UpsertProfile mirrors the location semantics of the real SQL: an explicit
47+ // clear drops the coordinates, new coordinates replace them, and a write that
48+ // carries neither leaves whatever was stored untouched.
4649func (r * fakeProfileRepo ) UpsertProfile (_ context.Context , p * domainprofile.Profile ) error {
4750 copied := * p
51+ switch {
52+ case p .ClearLocation :
53+ copied .HasLocation = false
54+ case p .Location != nil :
55+ copied .HasLocation = true
56+ default :
57+ if existing , ok := r .profiles [p .UserID ]; ok {
58+ copied .HasLocation = existing .HasLocation
59+ }
60+ }
4861 r .profiles [p .UserID ] = & copied
4962 return nil
5063}
@@ -298,6 +311,105 @@ func TestUpdateProfileRequiresBioAndPhotoToCompleteOnboarding(t *testing.T) {
298311 require .ErrorIs (t , err , applicationprofile .ErrOnboardingIncomplete )
299312}
300313
314+ // --- location intent ------------------------------------------------------
315+
316+ func coord (value float64 ) * float64 { return & value }
317+
318+ // TestUpdateProfileWithoutCoordinatesPreservesStoredLocation is the
319+ // regression test for the bug that made every profile undiscoverable: saving
320+ // the profile from the UI carried no coordinates and wiped the stored ones.
321+ func TestUpdateProfileWithoutCoordinatesPreservesStoredLocation (t * testing.T ) {
322+ repo := newFakeProfileRepo ()
323+ svc := newService (repo , newFakeStorage (), fakeConsentChecker {})
324+ userID := uuid .New ()
325+
326+ stored , err := svc .UpdateProfile (context .Background (), userID , applicationprofile.UpdateProfileInput {
327+ Bio : "con ubicación" , Latitude : coord (40.4168 ), Longitude : coord (- 3.7038 ),
328+ })
329+ require .NoError (t , err )
330+ require .True (t , stored .HasLocation )
331+
332+ // A later save that only edits the bio must not erase the location.
333+ updated , err := svc .UpdateProfile (context .Background (), userID , applicationprofile.UpdateProfileInput {
334+ Bio : "bio editada sin tocar la ubicación" ,
335+ })
336+ require .NoError (t , err )
337+ require .True (t , updated .HasLocation , "omitting coordinates must preserve the stored location" )
338+ }
339+
340+ func TestUpdateProfileClearLocationRemovesIt (t * testing.T ) {
341+ repo := newFakeProfileRepo ()
342+ svc := newService (repo , newFakeStorage (), fakeConsentChecker {})
343+ userID := uuid .New ()
344+
345+ _ , err := svc .UpdateProfile (context .Background (), userID , applicationprofile.UpdateProfileInput {
346+ Bio : "con ubicación" , Latitude : coord (40.4168 ), Longitude : coord (- 3.7038 ),
347+ })
348+ require .NoError (t , err )
349+
350+ cleared , err := svc .UpdateProfile (context .Background (), userID , applicationprofile.UpdateProfileInput {
351+ Bio : "sin ubicación" , ClearLocation : true ,
352+ })
353+ require .NoError (t , err )
354+ require .False (t , cleared .HasLocation )
355+ }
356+
357+ func TestUpdateProfileRejectsHalfCoordinates (t * testing.T ) {
358+ repo := newFakeProfileRepo ()
359+ svc := newService (repo , newFakeStorage (), fakeConsentChecker {})
360+
361+ _ , err := svc .UpdateProfile (context .Background (), uuid .New (), applicationprofile.UpdateProfileInput {
362+ Bio : "solo latitud" , Latitude : coord (40.4168 ),
363+ })
364+ require .ErrorIs (t , err , domainprofile .ErrIncompleteCoordinates )
365+
366+ _ , err = svc .UpdateProfile (context .Background (), uuid .New (), applicationprofile.UpdateProfileInput {
367+ Bio : "solo longitud" , Longitude : coord (- 3.7038 ),
368+ })
369+ require .ErrorIs (t , err , domainprofile .ErrIncompleteCoordinates )
370+ }
371+
372+ func TestUpdateProfileRejectsOutOfRangeCoordinates (t * testing.T ) {
373+ repo := newFakeProfileRepo ()
374+ svc := newService (repo , newFakeStorage (), fakeConsentChecker {})
375+
376+ _ , err := svc .UpdateProfile (context .Background (), uuid .New (), applicationprofile.UpdateProfileInput {
377+ Bio : "latitud imposible" , Latitude : coord (91 ), Longitude : coord (0 ),
378+ })
379+ require .ErrorIs (t , err , domainprofile .ErrIncompleteCoordinates )
380+ }
381+
382+ func TestUpdateProfileRejectsCoordinatesCombinedWithClear (t * testing.T ) {
383+ repo := newFakeProfileRepo ()
384+ svc := newService (repo , newFakeStorage (), fakeConsentChecker {})
385+
386+ _ , err := svc .UpdateProfile (context .Background (), uuid .New (), applicationprofile.UpdateProfileInput {
387+ Bio : "contradictorio" , Latitude : coord (40.4168 ), Longitude : coord (- 3.7038 ), ClearLocation : true ,
388+ })
389+ require .ErrorIs (t , err , domainprofile .ErrConflictingLocation )
390+ }
391+
392+ // TestUpdateProfileCompletesOnboardingWithoutLocation pins the deliberate
393+ // decision that a location is not required to finish onboarding.
394+ func TestUpdateProfileCompletesOnboardingWithoutLocation (t * testing.T ) {
395+ repo := newFakeProfileRepo ()
396+ store := newFakeStorage ()
397+ svc := newService (repo , store , fakeConsentChecker {})
398+ userID := uuid .New ()
399+
400+ _ , err := svc .CreatePhoto (context .Background (), userID , applicationprofile.NewPhotoInput {
401+ MimeType : "image/png" , Width : 10 , Height : 10 , ByteSize : 1 , Data : []byte ("x" ),
402+ })
403+ require .NoError (t , err )
404+
405+ updated , err := svc .UpdateProfile (context .Background (), userID , applicationprofile.UpdateProfileInput {
406+ Bio : "sin ubicación pero completo" , OnboardingCompleted : true ,
407+ })
408+ require .NoError (t , err )
409+ require .True (t , updated .OnboardingCompleted )
410+ require .False (t , updated .HasLocation )
411+ }
412+
301413func TestCreatePhotoAssignsFirstPhotoAsPrimary (t * testing.T ) {
302414 repo := newFakeProfileRepo ()
303415 store := newFakeStorage ()
0 commit comments