Skip to content

Commit b55c194

Browse files
committed
test: web-api JSON patch side-effect coverage across entities
1 parent 48b23fb commit b55c194

1 file changed

Lines changed: 157 additions & 0 deletions

File tree

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Copyright (c) 2004-2026, University of Oslo
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
*
15+
* 3. Neither the name of the copyright holder nor the names of its contributors
16+
* may be used to endorse or promote products derived from this software without
17+
* specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
package org.hisp.dhis.webapi.controller;
31+
32+
import static org.hisp.dhis.http.HttpAssertions.assertStatus;
33+
import static org.junit.jupiter.api.Assertions.assertEquals;
34+
35+
import org.hisp.dhis.http.HttpStatus;
36+
import org.hisp.dhis.jsontree.JsonObject;
37+
import org.hisp.dhis.organisationunit.OrganisationUnit;
38+
import org.hisp.dhis.test.webapi.H2ControllerIntegrationTestBase;
39+
import org.hisp.dhis.user.User;
40+
import org.hisp.dhis.user.UserRole;
41+
import org.junit.jupiter.api.DisplayName;
42+
import org.junit.jupiter.api.Test;
43+
import org.springframework.transaction.annotation.Transactional;
44+
45+
/**
46+
* HTTP-level regressions for system-wide JsonPatchManager non-owner collection skipping
47+
* (DHIS2-21852 / PR #24489). Complements {@link org.hisp.dhis.jsonpatch.JsonPatchManagerTest}.
48+
*
49+
* @author Morten (netroms)
50+
*/
51+
@Transactional
52+
class JsonPatchSideEffectControllerTest extends H2ControllerIntegrationTestBase {
53+
54+
@Test
55+
@DisplayName("PATCH organisationUnit name keeps children")
56+
void testPatchOrganisationUnitNameKeepsChildren() {
57+
OrganisationUnit parent = createOrganisationUnit('P');
58+
manager.save(parent);
59+
OrganisationUnit child = createOrganisationUnit('C', parent);
60+
manager.save(child);
61+
manager.update(parent);
62+
63+
assertStatus(
64+
HttpStatus.OK,
65+
PATCH(
66+
"/organisationUnits/" + parent.getUid(),
67+
"[{'op':'replace','path':'/name','value':'ParentPatched'}]"));
68+
69+
JsonObject body =
70+
GET("/organisationUnits/{id}?fields=name,children[id]", parent.getUid())
71+
.content(HttpStatus.OK);
72+
73+
assertEquals("ParentPatched", body.getString("name").string());
74+
assertEquals(1, body.getArray("children").size());
75+
assertEquals(child.getUid(), body.getArray("children").getObject(0).getString("id").string());
76+
}
77+
78+
@Test
79+
@DisplayName("PATCH userGroup name keeps owner users")
80+
void testPatchUserGroupNameKeepsUsers() {
81+
User user = makeUser("G");
82+
userService.addUser(user);
83+
84+
String groupId =
85+
assertStatus(
86+
HttpStatus.CREATED,
87+
POST(
88+
"/userGroups/",
89+
"{'name':'GroupSide','users':[{'id':'" + user.getUid() + "'}]}"));
90+
91+
assertStatus(
92+
HttpStatus.OK,
93+
PATCH(
94+
"/userGroups/" + groupId,
95+
"[{'op':'replace','path':'/name','value':'GroupSideRenamed'}]"));
96+
97+
JsonObject body =
98+
GET("/userGroups/{id}?fields=name,users[id]", groupId).content(HttpStatus.OK);
99+
100+
assertEquals("GroupSideRenamed", body.getString("name").string());
101+
assertEquals(1, body.getArray("users").size());
102+
assertEquals(user.getUid(), body.getArray("users").getObject(0).getString("id").string());
103+
}
104+
105+
@Test
106+
@DisplayName("PATCH user firstName keeps userGroups")
107+
void testPatchUserFirstNameKeepsUserGroups() {
108+
UserRole role = createUserRole('F');
109+
manager.save(role);
110+
111+
User user = makeUser("F");
112+
user.setEmail("first@example.org");
113+
user.getUserRoles().add(role);
114+
userService.addUser(user);
115+
116+
String groupId =
117+
assertStatus(
118+
HttpStatus.CREATED,
119+
POST(
120+
"/userGroups/",
121+
"{'name':'UserFirstGroup','users':[{'id':'" + user.getUid() + "'}]}"));
122+
123+
assertStatus(
124+
HttpStatus.OK,
125+
PATCH(
126+
"/users/" + user.getUid() + "?importReportMode=ERRORS",
127+
"[{'op':'replace','path':'/firstName','value':'FirstPatched'}]"));
128+
129+
JsonObject body =
130+
GET("/users/{id}?fields=firstName,userGroups[id]", user.getUid()).content(HttpStatus.OK);
131+
132+
assertEquals("FirstPatched", body.getString("firstName").string());
133+
assertEquals(1, body.getArray("userGroups").size());
134+
assertEquals(groupId, body.getArray("userGroups").getObject(0).getString("id").string());
135+
}
136+
137+
@Test
138+
@DisplayName("PATCH userRole replace /users does not change membership")
139+
void testPatchUserRoleUsersPathDoesNotChangeMembership() {
140+
UserRole role = createUserRole('S');
141+
User user = makeUser("R");
142+
userService.addUser(user);
143+
role.addUser(user);
144+
manager.save(role);
145+
146+
// Non-owner path: may return OK with ERRORS_NOT_OWNER notes; membership must not drop.
147+
PATCH(
148+
"/userRoles/" + role.getUid(),
149+
"[{'op':'replace','path':'/users','value':[]}]");
150+
151+
JsonObject body =
152+
GET("/userRoles/{id}?fields=users[id]", role.getUid()).content(HttpStatus.OK);
153+
154+
assertEquals(1, body.getArray("users").size());
155+
assertEquals(user.getUid(), body.getArray("users").getObject(0).getString("id").string());
156+
}
157+
}

0 commit comments

Comments
 (0)