实现快传,完善快传和网盘的功能,实现文件的互传等一系列功能
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package com.yoyuzh.transfer;
|
||||
|
||||
import com.yoyuzh.PortalBackendApplication;
|
||||
import com.yoyuzh.auth.User;
|
||||
import com.yoyuzh.auth.UserRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
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.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
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(
|
||||
classes = PortalBackendApplication.class,
|
||||
properties = {
|
||||
"spring.datasource.url=jdbc:h2:mem:transfer_api_test;MODE=MySQL;DB_CLOSE_DELAY=-1",
|
||||
"spring.datasource.driver-class-name=org.h2.Driver",
|
||||
"spring.datasource.username=sa",
|
||||
"spring.datasource.password=",
|
||||
"spring.jpa.hibernate.ddl-auto=create-drop",
|
||||
"app.jwt.secret=0123456789abcdef0123456789abcdef",
|
||||
"app.storage.root-dir=./target/test-storage-transfer"
|
||||
}
|
||||
)
|
||||
@AutoConfigureMockMvc
|
||||
class TransferControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
userRepository.deleteAll();
|
||||
|
||||
User portalUser = new User();
|
||||
portalUser.setUsername("alice");
|
||||
portalUser.setEmail("alice@example.com");
|
||||
portalUser.setPhoneNumber("13800138000");
|
||||
portalUser.setPasswordHash("encoded-password");
|
||||
portalUser.setCreatedAt(LocalDateTime.now());
|
||||
userRepository.save(portalUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "alice")
|
||||
void shouldCreateLookupJoinAndPollTransferSignals() throws Exception {
|
||||
String response = mockMvc.perform(post("/api/transfer/sessions")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"files": [
|
||||
{"name": "report.pdf", "size": 2048, "contentType": "application/pdf"}
|
||||
]
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(0))
|
||||
.andExpect(jsonPath("$.data.sessionId").isNotEmpty())
|
||||
.andExpect(jsonPath("$.data.pickupCode").isString())
|
||||
.andExpect(jsonPath("$.data.files[0].name").value("report.pdf"))
|
||||
.andReturn()
|
||||
.getResponse()
|
||||
.getContentAsString();
|
||||
|
||||
String sessionId = com.jayway.jsonpath.JsonPath.read(response, "$.data.sessionId");
|
||||
String pickupCode = com.jayway.jsonpath.JsonPath.read(response, "$.data.pickupCode");
|
||||
|
||||
mockMvc.perform(get("/api/transfer/sessions/lookup").param("pickupCode", pickupCode))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.sessionId").value(sessionId))
|
||||
.andExpect(jsonPath("$.data.pickupCode").value(pickupCode));
|
||||
|
||||
mockMvc.perform(post("/api/transfer/sessions/{sessionId}/join", sessionId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.sessionId").value(sessionId))
|
||||
.andExpect(jsonPath("$.data.files[0].name").value("report.pdf"));
|
||||
|
||||
mockMvc.perform(post("/api/transfer/sessions/{sessionId}/signals", sessionId)
|
||||
.param("role", "sender")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"type": "offer",
|
||||
"payload": "{\\\"sdp\\\":\\\"demo-offer\\\"}"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(0));
|
||||
|
||||
mockMvc.perform(get("/api/transfer/sessions/{sessionId}/signals", sessionId)
|
||||
.param("role", "receiver")
|
||||
.param("after", "0"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.items[0].type").value("offer"))
|
||||
.andExpect(jsonPath("$.data.items[0].payload").value("{\"sdp\":\"demo-offer\"}"))
|
||||
.andExpect(jsonPath("$.data.nextCursor").value(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectAnonymousSessionCreationButAllowPublicJoinEndpoints() throws Exception {
|
||||
mockMvc.perform(post("/api/transfer/sessions")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"files":[{"name":"demo.txt","size":12,"contentType":"text/plain"}]}
|
||||
"""))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
mockMvc.perform(post("/api/transfer/sessions/{sessionId}/join", "missing-session"))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.yoyuzh.transfer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TransferSessionTest {
|
||||
|
||||
@Test
|
||||
void shouldEmitPeerJoinedOnlyOnceWhenReceiverJoinsRepeatedly() {
|
||||
TransferSession session = new TransferSession(
|
||||
"session-1",
|
||||
"849201",
|
||||
Instant.parse("2026-03-20T12:00:00Z"),
|
||||
List.of(new TransferFileItem("report.pdf", 2048, "application/pdf"))
|
||||
);
|
||||
|
||||
session.markReceiverJoined();
|
||||
session.markReceiverJoined();
|
||||
|
||||
PollTransferSignalsResponse senderSignals = session.poll(TransferRole.SENDER, 0);
|
||||
|
||||
assertThat(senderSignals.items())
|
||||
.extracting(TransferSignalEnvelope::type)
|
||||
.containsExactly("peer-joined");
|
||||
assertThat(senderSignals.nextCursor()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRouteSignalsToTheOppositeRoleQueue() {
|
||||
TransferSession session = new TransferSession(
|
||||
"session-1",
|
||||
"849201",
|
||||
Instant.parse("2026-03-20T12:00:00Z"),
|
||||
List.of(new TransferFileItem("report.pdf", 2048, "application/pdf"))
|
||||
);
|
||||
|
||||
session.enqueue(TransferRole.SENDER, "offer", "{\"sdp\":\"demo-offer\"}");
|
||||
session.enqueue(TransferRole.RECEIVER, "answer", "{\"sdp\":\"demo-answer\"}");
|
||||
|
||||
PollTransferSignalsResponse receiverSignals = session.poll(TransferRole.RECEIVER, 0);
|
||||
PollTransferSignalsResponse senderSignals = session.poll(TransferRole.SENDER, 0);
|
||||
|
||||
assertThat(receiverSignals.items())
|
||||
.extracting(TransferSignalEnvelope::type, TransferSignalEnvelope::payload)
|
||||
.containsExactly(org.assertj.core.groups.Tuple.tuple("offer", "{\"sdp\":\"demo-offer\"}"));
|
||||
assertThat(senderSignals.items())
|
||||
.extracting(TransferSignalEnvelope::type, TransferSignalEnvelope::payload)
|
||||
.containsExactly(org.assertj.core.groups.Tuple.tuple("answer", "{\"sdp\":\"demo-answer\"}"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user