@@ -958,3 +958,124 @@ func TestPeopleHandlerGetWorkerTasksRequiresRuntimeLease(t *testing.T) {
958958 resp := decodeAPIResponse (t , rec )
959959 require .False (t , resp .Success )
960960}
961+
962+ // TestPeopleHandler_ListPeopleVisibility 验证 visibility 查询参数与响应 hidden 字段。
963+ func TestPeopleHandler_ListPeopleVisibility (t * testing.T ) {
964+ handler , _ , _ , db , _ := newPeopleHandlerForTest (t )
965+ fixture := seedPeopleHandlerFixture (t , db )
966+
967+ // 将 friend 标记为隐藏
968+ require .NoError (t , db .Model (& model.Person {}).Where ("id = ?" , fixture .FriendPerson .ID ).Update ("hidden" , true ).Error )
969+
970+ // visible:仅返回 family(显示中)
971+ rec := performJSONRequest (t , http .MethodGet , "/api/v1/people?visibility=visible&page=1&page_size=20" , nil , nil , handler .ListPeople )
972+ require .Equal (t , http .StatusOK , rec .Code )
973+ resp := decodeAPIResponse (t , rec )
974+ require .True (t , resp .Success )
975+ payload := decodeResponseData [peopleListPayload ](t , resp )
976+ assert .Equal (t , int64 (1 ), payload .Total )
977+ require .Len (t , payload .Items , 1 )
978+ assert .Equal (t , fixture .FamilyPerson .ID , payload .Items [0 ].ID )
979+ assert .False (t , payload .Items [0 ].Hidden )
980+
981+ // hidden:仅返回 friend(已隐藏)
982+ rec = performJSONRequest (t , http .MethodGet , "/api/v1/people?visibility=hidden&page=1&page_size=20" , nil , nil , handler .ListPeople )
983+ require .Equal (t , http .StatusOK , rec .Code )
984+ resp = decodeAPIResponse (t , rec )
985+ payload = decodeResponseData [peopleListPayload ](t , resp )
986+ assert .Equal (t , int64 (1 ), payload .Total )
987+ require .Len (t , payload .Items , 1 )
988+ assert .Equal (t , fixture .FriendPerson .ID , payload .Items [0 ].ID )
989+ assert .True (t , payload .Items [0 ].Hidden )
990+
991+ // all:返回全部
992+ rec = performJSONRequest (t , http .MethodGet , "/api/v1/people?visibility=all&page=1&page_size=20" , nil , nil , handler .ListPeople )
993+ require .Equal (t , http .StatusOK , rec .Code )
994+ resp = decodeAPIResponse (t , rec )
995+ payload = decodeResponseData [peopleListPayload ](t , resp )
996+ assert .Equal (t , int64 (2 ), payload .Total )
997+ assert .Len (t , payload .Items , 2 )
998+
999+ // 缺省按 all 处理
1000+ rec = performJSONRequest (t , http .MethodGet , "/api/v1/people?page=1&page_size=20" , nil , nil , handler .ListPeople )
1001+ require .Equal (t , http .StatusOK , rec .Code )
1002+ resp = decodeAPIResponse (t , rec )
1003+ payload = decodeResponseData [peopleListPayload ](t , resp )
1004+ assert .Equal (t , int64 (2 ), payload .Total )
1005+ }
1006+
1007+ // TestPeopleHandler_UpdateVisibility 验证批量隐藏/恢复、参数校验、不存在 ID 行为及副作用隔离。
1008+ func TestPeopleHandler_UpdateVisibility (t * testing.T ) {
1009+ handler , _ , _ , db , _ := newPeopleHandlerForTest (t )
1010+ fixture := seedPeopleHandlerFixture (t , db )
1011+
1012+ // 记录 family 原始分类,用于验证隐藏操作不修改分类
1013+ originalCategory := fixture .FamilyPerson .Category
1014+
1015+ t .Run ("batch hide then restore" , func (t * testing.T ) {
1016+ body := []byte (`{"person_ids":[` + strconv .FormatUint (uint64 (fixture .FamilyPerson .ID ), 10 ) + `],"hidden":true}` )
1017+ rec := performJSONRequest (t , http .MethodPatch , "/api/v1/people/visibility" , body , nil , handler .UpdateVisibility )
1018+ require .Equal (t , http .StatusOK , rec .Code )
1019+ resp := decodeAPIResponse (t , rec )
1020+ require .True (t , resp .Success )
1021+ data := decodeResponseData [map [string ]interface {}](t , resp )
1022+ assert .EqualValues (t , 1 , data ["updated" ])
1023+
1024+ // 验证 DB:hidden=true,category 未变
1025+ var got model.Person
1026+ require .NoError (t , db .First (& got , fixture .FamilyPerson .ID ).Error )
1027+ assert .True (t , got .Hidden )
1028+ assert .Equal (t , originalCategory , got .Category )
1029+
1030+ // 恢复
1031+ body = []byte (`{"person_ids":[` + strconv .FormatUint (uint64 (fixture .FamilyPerson .ID ), 10 ) + `],"hidden":false}` )
1032+ rec = performJSONRequest (t , http .MethodPatch , "/api/v1/people/visibility" , body , nil , handler .UpdateVisibility )
1033+ require .Equal (t , http .StatusOK , rec .Code )
1034+ require .NoError (t , db .First (& got , fixture .FamilyPerson .ID ).Error )
1035+ assert .False (t , got .Hidden )
1036+ })
1037+
1038+ t .Run ("dedup and missing ids" , func (t * testing.T ) {
1039+ // 部分不存在:更新存在的,返回 updated=1,missing_count=1
1040+ body := []byte (`{"person_ids":[` + strconv .FormatUint (uint64 (fixture .FriendPerson .ID ), 10 ) + `,999999],"hidden":true}` )
1041+ rec := performJSONRequest (t , http .MethodPatch , "/api/v1/people/visibility" , body , nil , handler .UpdateVisibility )
1042+ require .Equal (t , http .StatusOK , rec .Code )
1043+ resp := decodeAPIResponse (t , rec )
1044+ require .True (t , resp .Success )
1045+ data := decodeResponseData [map [string ]interface {}](t , resp )
1046+ assert .EqualValues (t , 1 , data ["updated" ])
1047+ assert .EqualValues (t , 1 , data ["missing_count" ])
1048+
1049+ // 全部不存在:404
1050+ body = []byte (`{"person_ids":[999998,999999],"hidden":true}` )
1051+ rec = performJSONRequest (t , http .MethodPatch , "/api/v1/people/visibility" , body , nil , handler .UpdateVisibility )
1052+ assert .Equal (t , http .StatusNotFound , rec .Code )
1053+ })
1054+
1055+ t .Run ("empty person_ids rejected" , func (t * testing.T ) {
1056+ body := []byte (`{"person_ids":[],"hidden":true}` )
1057+ rec := performJSONRequest (t , http .MethodPatch , "/api/v1/people/visibility" , body , nil , handler .UpdateVisibility )
1058+ assert .Equal (t , http .StatusBadRequest , rec .Code )
1059+ })
1060+
1061+ t .Run ("missing hidden field rejected" , func (t * testing.T ) {
1062+ body := []byte (`{"person_ids":[` + strconv .FormatUint (uint64 (fixture .FamilyPerson .ID ), 10 ) + `]}` )
1063+ rec := performJSONRequest (t , http .MethodPatch , "/api/v1/people/visibility" , body , nil , handler .UpdateVisibility )
1064+ assert .Equal (t , http .StatusBadRequest , rec .Code )
1065+ })
1066+
1067+ t .Run ("hidden does not change top_person_category" , func (t * testing.T ) {
1068+ // 记录 photoOne 的 top_person_category,隐藏 family 后应保持不变
1069+ var photoBefore model.Photo
1070+ require .NoError (t , db .First (& photoBefore , fixture .PhotoOne .ID ).Error )
1071+ categoryBefore := photoBefore .TopPersonCategory
1072+
1073+ body := []byte (`{"person_ids":[` + strconv .FormatUint (uint64 (fixture .FamilyPerson .ID ), 10 ) + `],"hidden":true}` )
1074+ rec := performJSONRequest (t , http .MethodPatch , "/api/v1/people/visibility" , body , nil , handler .UpdateVisibility )
1075+ require .Equal (t , http .StatusOK , rec .Code )
1076+
1077+ var photoAfter model.Photo
1078+ require .NoError (t , db .First (& photoAfter , fixture .PhotoOne .ID ).Error )
1079+ assert .Equal (t , categoryBefore , photoAfter .TopPersonCategory )
1080+ })
1081+ }
0 commit comments