降低订阅快照测试耦合并统一缓存路径

This commit is contained in:
root
2026-08-30 10:41:55 +08:00
parent 10e6d44570
commit fb327ba00c
4 changed files with 33 additions and 19 deletions
@@ -41,6 +41,9 @@ public class SubscriptionRefreshService {
@Value("${subscription.upstream.high-multiplier-threshold:2.0}")
double highMultiplierThreshold;
@Value("${subscription.cache-root:sub/accounts}")
String cacheRoot;
public boolean refreshAll() {
boolean success = true;
for (SubscriptionAccount account : subMapper.selectAllSubscriptionAccounts()) {
@@ -60,7 +63,7 @@ public class SubscriptionRefreshService {
try {
String v2 = processV2(firstLine(download(v2Url(account))), account.isFilterHighMultiplier(), highMultiplierThreshold);
List<String> clash = processClash(download(clashUrl(account)), account.isFilterHighMultiplier(), highMultiplierThreshold);
Path dir = Paths.get("sub", "accounts", String.valueOf(accountId));
Path dir = Paths.get(cacheRoot, String.valueOf(accountId));
Files.createDirectories(dir);
atomicWrite(dir.resolve("v2ray.txt"), v2.getBytes(StandardCharsets.UTF_8));
atomicWrite(dir.resolve("clash.yaml"), String.join("\n", clash).concat("\n").getBytes(StandardCharsets.UTF_8));
@@ -205,7 +208,7 @@ public class SubscriptionRefreshService {
}
public Path cachedPath(Integer accountId, String client) {
return Paths.get("sub", "accounts", String.valueOf(accountId), client.equals("v2") ? "v2ray.txt" : "clash.yaml");
return Paths.get(cacheRoot, String.valueOf(accountId), client.equals("v2") ? "v2ray.txt" : "clash.yaml");
}
public boolean hasCompleteCache(Integer accountId) {
@@ -27,7 +27,6 @@ import java.util.concurrent.locks.Lock;
@RequiredArgsConstructor
public class SubscriptionStandbySnapshotService {
private final SubMapper subMapper;
private final SubscriptionRefreshService refreshService;
private final SubscriptionStateCoordinator stateCoordinator;
private final ObjectMapper objectMapper = CustomUtil.objectMapper;
@@ -37,6 +36,9 @@ public class SubscriptionStandbySnapshotService {
@Value("${subscription.standby.sync-enabled:false}")
boolean syncEnabled;
@Value("${subscription.cache-root:sub/accounts}")
String cacheRoot;
@PostConstruct
void validateConfiguration() {
if (syncEnabled && (syncSecret == null || syncSecret.isBlank()))
@@ -49,10 +51,10 @@ public class SubscriptionStandbySnapshotService {
try {
Map<Integer, SubscriptionAccountSnapshot> accountMap = new HashMap<>();
for (SubscriptionAccount account : subMapper.selectAllSubscriptionAccounts()) {
if (!account.isEnabled() || !refreshService.hasCompleteCache(account.getId()))
Path v2Path = cachedPath(account.getId(), "v2");
Path clashPath = cachedPath(account.getId(), "cat");
if (!account.isEnabled() || !Files.isRegularFile(v2Path) || !Files.isRegularFile(clashPath))
continue;
Path v2Path = refreshService.cachedPath(account.getId(), "v2");
Path clashPath = refreshService.cachedPath(account.getId(), "cat");
byte[] v2 = Files.readAllBytes(v2Path);
byte[] clash = Files.readAllBytes(clashPath);
SubscriptionAccountSnapshot snapshot = new SubscriptionAccountSnapshot();
@@ -129,4 +131,8 @@ public class SubscriptionStandbySnapshotService {
}
private static String hex(byte[] input) { return HexFormat.of().formatHex(input); }
private Path cachedPath(Integer accountId, String client) {
return Path.of(cacheRoot, String.valueOf(accountId), "v2".equals(client) ? "v2ray.txt" : "clash.yaml");
}
}
+1
View File
@@ -37,6 +37,7 @@ local:
dou-nai-v2ray: "https://aaaa.gay/link/{key}?client=v2"
subscription:
cache-root: sub/accounts
upstream:
# Use environment variables or an external config file in production.
v2-url-template: "https://aaaa.gay/link/{key}?client=v2"
@@ -13,35 +13,39 @@ import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Base64;
import java.util.zip.GZIPInputStream;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class SubscriptionStandbySnapshotServiceTest {
@Test
void buildsDeterministicSnapshotWithoutPlainPublicKeys(@TempDir Path directory) throws Exception {
SubMapper mapper = mock(SubMapper.class);
SubscriptionRefreshService refreshService = mock(SubscriptionRefreshService.class);
SubscriptionStateCoordinator coordinator = new SubscriptionStateCoordinator();
SubscriptionAccount account = new SubscriptionAccount(1, "account", "upstream-secret", true, true,
null, null, null, null, 2, null, null);
when(mapper.selectAllSubscriptionAccounts()).thenReturn(new ArrayList<>(java.util.List.of(account)));
when(mapper.selectAllSubBind()).thenReturn(new ArrayList<>(java.util.List.of(
ArrayList<SubscriptionAccount> accounts = new ArrayList<>(java.util.List.of(account));
ArrayList<SubBind> bindings = new ArrayList<>(java.util.List.of(
new SubBind("public-key-a", "user-a", 1, "account", true, true),
new SubBind("public-key-b", "user-b", 1, "account", true, true))));
Path v2 = directory.resolve("v2ray.txt");
Path clash = directory.resolve("clash.yaml");
new SubBind("public-key-b", "user-b", 1, "account", true, true)));
SubMapper mapper = (SubMapper) Proxy.newProxyInstance(SubMapper.class.getClassLoader(),
new Class<?>[]{SubMapper.class}, (proxy, method, args) -> switch (method.getName()) {
case "selectAllSubscriptionAccounts" -> accounts;
case "selectAllSubBind" -> bindings;
default -> throw new UnsupportedOperationException(method.getName());
});
SubscriptionStateCoordinator coordinator = new SubscriptionStateCoordinator();
Path accountDirectory = directory.resolve("1");
Files.createDirectories(accountDirectory);
Path v2 = accountDirectory.resolve("v2ray.txt");
Path clash = accountDirectory.resolve("clash.yaml");
Files.writeString(v2, "v2-content", StandardCharsets.UTF_8);
Files.writeString(clash, "clash-content", StandardCharsets.UTF_8);
when(refreshService.hasCompleteCache(1)).thenReturn(true);
when(refreshService.cachedPath(1, "v2")).thenReturn(v2);
when(refreshService.cachedPath(1, "cat")).thenReturn(clash);
SubscriptionStandbySnapshotService service = new SubscriptionStandbySnapshotService(mapper, refreshService, coordinator);
SubscriptionStandbySnapshotService service = new SubscriptionStandbySnapshotService(mapper, coordinator);
service.syncSecret = "test-sync-secret";
service.cacheRoot = directory.toString();
SubscriptionSnapshotMessage first = service.build();
SubscriptionSnapshotMessage second = service.build();