Skip to content

Commit 4fd9c05

Browse files
Matus Kasakclaude
andcommitted
Add regression test for the CLARIN link-repository bean naming contract
Nothing in the suite caught the previous commit's bug: no IT traverses any of the six rels as a URL sub-path, so all six could 404 in production while CI stayed green. It surfaced only in the external dspace-rest-test suite, as 8 failing test_endpoints tests whose messages looked like an authorization problem. The test asserts the invariant directly through Utils.getLinkResourceRepository() -- the same lookup RestResourceController performs when a client traverses a rel -- rather than over HTTP. Going over HTTP could not isolate this defect: these link repositories also raise ResourceNotFoundException, another 404, when the linked data simply does not exist, so telling "route missing" from "no data" would need fixtures for a bitstream, a licence, a resource mapping, a user registration and user metadata per rel, and would still conflate the two on failure. It is driven off the @LinksRest annotation instead of a hardcoded rel list, so a rel added to any of these three models is covered automatically. Covers all six rels: clarinlruallowances {resourceMapping, userRegistration, userMetadata}, clarinuserregistrations {clarinLicenses, userMetadata}, clarinlicenseresourcemappings {clarinLicense}. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent aec8451 commit 4fd9c05

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
package org.dspace.app.rest;
9+
10+
import static org.junit.Assert.assertNotNull;
11+
import static org.junit.Assert.assertTrue;
12+
13+
import org.dspace.app.rest.model.ClarinLicenseResourceMappingRest;
14+
import org.dspace.app.rest.model.ClarinLicenseResourceUserAllowanceRest;
15+
import org.dspace.app.rest.model.ClarinUserRegistrationRest;
16+
import org.dspace.app.rest.model.LinkRest;
17+
import org.dspace.app.rest.model.LinksRest;
18+
import org.dspace.app.rest.model.RestAddressableModel;
19+
import org.dspace.app.rest.repository.LinkRestRepository;
20+
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
21+
import org.dspace.app.rest.utils.Utils;
22+
import org.junit.Test;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
25+
/**
26+
* Guards the DSpace 9 link-repository bean naming contract for the CLARIN models.
27+
* <P>
28+
* DSpace 7 resolved a rel by singularizing the URL segment before the bean lookup
29+
* ({@code Utils.getLinkResourceRepository} called {@code makeSingular}). DSpace 9 removed that
30+
* step and looks the bean up under the plural segment verbatim, so every
31+
* {@link LinkRestRepository} must be registered as
32+
* {@code <category>.<typePlural>.<rel>}. A repository still registered under the singular name
33+
* is simply never found: the lookup raises {@code RepositoryNotFoundException}, the client gets
34+
* a 404, and because a missing route resolves BEFORE any authorization check the same defect
35+
* shows up as "404 instead of 200" for an admin and "404 instead of 401/403" for everyone else.
36+
* It therefore reads like an authorization bug while the object's own {@code _links} keep
37+
* advertising the dead rels.
38+
* <P>
39+
* This test asserts the invariant directly instead of going through HTTP, because the link
40+
* repositories also raise {@code ResourceNotFoundException} (another 404) when the linked data
41+
* simply does not exist -- an endpoint test could not tell the two apart without fixtures for
42+
* every entity type. It is deliberately driven off the {@link LinksRest} annotation rather than
43+
* a hardcoded list, so a rel added to any of these models is covered automatically.
44+
*/
45+
public class ClarinLinkRestRepositoryBeanNameIT extends AbstractControllerIntegrationTest {
46+
47+
@Autowired
48+
private Utils utils;
49+
50+
/**
51+
* Asserts that every rel declared via {@link LinksRest} on the given model resolves to a
52+
* registered {@link LinkRestRepository}, using the same lookup {@link RestResourceController}
53+
* performs when a client traverses the rel.
54+
*
55+
* @param modelClass the REST model whose declared rels should all be resolvable
56+
*/
57+
private void assertAllDeclaredRelsResolve(Class<? extends RestAddressableModel> modelClass)
58+
throws ReflectiveOperationException {
59+
RestAddressableModel model = modelClass.getDeclaredConstructor().newInstance();
60+
LinksRest linksRest = modelClass.getDeclaredAnnotation(LinksRest.class);
61+
assertNotNull(modelClass.getSimpleName() + " is expected to declare @LinksRest", linksRest);
62+
assertTrue(modelClass.getSimpleName() + " is expected to declare at least one @LinkRest",
63+
linksRest.links().length > 0);
64+
65+
for (LinkRest linkRest : linksRest.links()) {
66+
String expectedBeanName = model.getCategory() + "." + model.getTypePlural() + "." + linkRest.name();
67+
// Throws RepositoryNotFoundException (-> HTTP 404) when the bean is registered under
68+
// the old singular name instead of the plural one.
69+
LinkRestRepository repository =
70+
utils.getLinkResourceRepository(model.getCategory(), model.getTypePlural(), linkRest.name());
71+
assertNotNull("No LinkRestRepository registered as '" + expectedBeanName + "'", repository);
72+
}
73+
}
74+
75+
@Test
76+
public void clarinLicenseResourceUserAllowanceRelsResolve() throws Exception {
77+
// resourceMapping, userRegistration, userMetadata
78+
assertAllDeclaredRelsResolve(ClarinLicenseResourceUserAllowanceRest.class);
79+
}
80+
81+
@Test
82+
public void clarinUserRegistrationRelsResolve() throws Exception {
83+
// clarinLicenses, userMetadata
84+
assertAllDeclaredRelsResolve(ClarinUserRegistrationRest.class);
85+
}
86+
87+
@Test
88+
public void clarinLicenseResourceMappingRelsResolve() throws Exception {
89+
// clarinLicense
90+
assertAllDeclaredRelsResolve(ClarinLicenseResourceMappingRest.class);
91+
}
92+
}

0 commit comments

Comments
 (0)