降低订阅快照测试耦合并统一缓存路径
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
+17
-13
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user