mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 18:10:30 +08:00
feat(platform): I5 callback inbox, internal ingest, and M6 catalog APIs
Made-with: Cursor
This commit is contained in:
+120
@@ -0,0 +1,120 @@
|
||||
package cn.craftlabs.platform.api.callback;
|
||||
|
||||
import cn.craftlabs.platform.api.support.JwtTestSupport;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@Transactional
|
||||
class CallbackInboxControllerTest {
|
||||
|
||||
private static final String INTERNAL_TOKEN = "unit-test-internal-token-for-callback-ingest";
|
||||
private static final String INTERNAL_HEADER = "X-Platform-Internal-Token";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Test
|
||||
void listDetailStatusLinkAndIntegrationCatalog() throws Exception {
|
||||
String token = JwtTestSupport.obtainBearerToken(mockMvc, objectMapper);
|
||||
String auth = "Bearer " + token;
|
||||
|
||||
mockMvc.perform(
|
||||
get("/api/v1/integration/product-lines")
|
||||
.header("Authorization", auth)
|
||||
.param("page", "0")
|
||||
.param("size", "10"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.content.length()").value(1))
|
||||
.andExpect(jsonPath("$.content[0].code").value("default"));
|
||||
|
||||
mockMvc.perform(
|
||||
get("/api/v1/integration/environments")
|
||||
.header("Authorization", auth)
|
||||
.param("page", "0")
|
||||
.param("size", "10"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.content.length()").value(2));
|
||||
|
||||
String ingestBody = minimalIngestJson("msg-inbox-flow-1");
|
||||
String ingestResp =
|
||||
mockMvc.perform(
|
||||
post("/internal/v1/callback-events")
|
||||
.header(INTERNAL_HEADER, INTERNAL_TOKEN)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(ingestBody))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getContentAsString();
|
||||
long inboxId = objectMapper.readTree(ingestResp).get("inboxId").asLong();
|
||||
|
||||
mockMvc.perform(
|
||||
get("/api/v1/callback-inbox")
|
||||
.header("Authorization", auth)
|
||||
.param("page", "0")
|
||||
.param("size", "20")
|
||||
.param("eventType", "sn:test"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.content.length()").value(1))
|
||||
.andExpect(jsonPath("$.content[0].id").value(inboxId))
|
||||
.andExpect(jsonPath("$.content[0].rawPayload").value(nullValue()));
|
||||
|
||||
mockMvc.perform(get("/api/v1/callback-inbox/" + inboxId).header("Authorization", auth))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.rawPayload").exists())
|
||||
.andExpect(jsonPath("$.status").value("PENDING"));
|
||||
|
||||
mockMvc.perform(
|
||||
patch("/api/v1/callback-inbox/" + inboxId + "/status")
|
||||
.header("Authorization", auth)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"status\":\"PROCESSED\"}"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status").value("PROCESSED"));
|
||||
|
||||
mockMvc.perform(
|
||||
patch("/api/v1/callback-inbox/" + inboxId + "/status")
|
||||
.header("Authorization", auth)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"status\":\"FAILED\"}"))
|
||||
.andExpect(status().isConflict())
|
||||
.andExpect(jsonPath("$.message").value(containsString("illegal")));
|
||||
|
||||
mockMvc.perform(
|
||||
patch("/api/v1/callback-inbox/" + inboxId + "/link")
|
||||
.header("Authorization", auth)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"licenseSnId\":999999}"))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
private String minimalIngestJson(String externalMessageId) throws Exception {
|
||||
ObjectNode root = objectMapper.createObjectNode();
|
||||
root.put("schemaVersion", "1.0");
|
||||
root.put("sourceSystem", "BITANSWER");
|
||||
root.put("externalMessageId", externalMessageId);
|
||||
root.put("eventType", "sn:test");
|
||||
root.set("rawPayload", objectMapper.createObjectNode().put("sn", "SN-X"));
|
||||
return objectMapper.writeValueAsString(root);
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package cn.craftlabs.platform.api.internal;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@Transactional
|
||||
class CallbackInternalControllerTest {
|
||||
|
||||
private static final String INTERNAL_TOKEN = "unit-test-internal-token-for-callback-ingest";
|
||||
private static final String INTERNAL_HEADER = "X-Platform-Internal-Token";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Test
|
||||
void unauthorizedWithoutToken() throws Exception {
|
||||
mockMvc.perform(
|
||||
post("/internal/v1/callback-events")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{}"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ingestIdempotentReturnsSameInboxId() throws Exception {
|
||||
String body = minimalIngestJson("msg-idempotent-1");
|
||||
String first =
|
||||
mockMvc.perform(
|
||||
post("/internal/v1/callback-events")
|
||||
.header(INTERNAL_HEADER, INTERNAL_TOKEN)
|
||||
.header("Idempotency-Key", "idem-1")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(body))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.duplicate").value(false))
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getContentAsString();
|
||||
long inboxId = objectMapper.readTree(first).get("inboxId").asLong();
|
||||
|
||||
mockMvc.perform(
|
||||
post("/internal/v1/callback-events")
|
||||
.header(INTERNAL_HEADER, INTERNAL_TOKEN)
|
||||
.header("Idempotency-Key", "idem-replay")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(body))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.duplicate").value(true))
|
||||
.andExpect(jsonPath("$.inboxId").value(inboxId));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsUnsupportedSchemaMajor() throws Exception {
|
||||
ObjectNode root = objectMapper.createObjectNode();
|
||||
root.put("schemaVersion", "2.0");
|
||||
root.put("sourceSystem", "BITANSWER");
|
||||
root.put("externalMessageId", "msg-major");
|
||||
root.put("eventType", "t");
|
||||
root.set("rawPayload", objectMapper.createObjectNode());
|
||||
mockMvc.perform(
|
||||
post("/internal/v1/callback-events")
|
||||
.header(INTERNAL_HEADER, INTERNAL_TOKEN)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(root)))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.message").value(containsString("unsupported schema major")));
|
||||
}
|
||||
|
||||
private String minimalIngestJson(String externalMessageId) throws Exception {
|
||||
ObjectNode root = objectMapper.createObjectNode();
|
||||
root.put("schemaVersion", "1.0");
|
||||
root.put("sourceSystem", "BITANSWER");
|
||||
root.put("externalMessageId", externalMessageId);
|
||||
root.put("eventType", "sn:test");
|
||||
root.set("rawPayload", objectMapper.createObjectNode().put("sn", "SN-X"));
|
||||
return objectMapper.writeValueAsString(root);
|
||||
}
|
||||
}
|
||||
@@ -12,3 +12,5 @@ spring:
|
||||
platform:
|
||||
jwt:
|
||||
secret: unit-test-jwt-secret-at-least-32-chars-ok
|
||||
internal:
|
||||
token: unit-test-internal-token-for-callback-ingest
|
||||
|
||||
Reference in New Issue
Block a user