feat(self-operated): expose private runtime configuration snapshots

This commit is contained in:
2026-10-13 22:23:48 +08:00
parent 1d0b0918ba
commit 54f79b3902
11 changed files with 356 additions and 11 deletions
@@ -4,6 +4,63 @@ import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.*;
class SelfopEgressControllerTest {
@Test void runtimeRequiresInternalTokenBeforeReadingConfiguration(){
var service=mock(SelfopEgressService.class);var c=new SelfopEgressController(new InternalServiceTokenGuard("synthetic"),service);
for(String token:new String[]{null,"","wrong"})assertThatThrownBy(()->c.runtime(token))
.isInstanceOf(org.springframework.web.server.ResponseStatusException.class)
.satisfies(e->assertThat(((org.springframework.web.server.ResponseStatusException)e).getStatusCode().value()).isEqualTo(401));
verifyNoInteractions(service);
var missing=new SelfopEgressController(new InternalServiceTokenGuard(""),service);
assertThatThrownBy(()->missing.runtime("synthetic")).isInstanceOf(org.springframework.web.server.ResponseStatusException.class)
.satisfies(e->assertThat(((org.springframework.web.server.ResponseStatusException)e).getStatusCode().value()).isEqualTo(503));
verifyNoInteractions(service);c.runtime("synthetic");verify(service).runtime();
}
@Test void runtimeUsesExecutionConfigurationAndSerializesOnlyWhitelistedFields() throws Exception {
var storage=mock(SelfopEgressMapper.class);var config=new com.baiyue.lastleg.selfoperated.analytics.model.SelfopAnalyticsProperties();
config.setCollectionMode("BUSINESS");config.setEnvironment("test");config.setContinuousBusinessCollectionEnabled(true);
config.setSourceAccount("synthetic-private-account");config.setExclusiveGrantId("synthetic-private-grant");
config.setInternalServiceToken("synthetic-private-token");config.setIntegrationBaseUrl("http://synthetic-private-host");
config.setBusinessMinRequestIntervalMs(1200);config.setEgressLeaseSeconds(999);
config.setExclusiveHandoffVerified(true);config.setPriorEgressClosed(true);config.setInflightDrained(true);config.setTransferredBudgetVerified(true);
var now=java.time.Instant.parse("2026-10-13T00:00:00Z");
var service=new SelfopEgressService(storage,config,java.time.Clock.fixed(now,java.time.ZoneOffset.UTC));
var controller=new SelfopEgressController(new InternalServiceTokenGuard("synthetic"),service);
var response=controller.runtime("synthetic");var runtime=response.data();
assertThat(runtime.schemaVersion()).isEqualTo(1);assertThat(runtime.service()).isEqualTo("lastleg");assertThat(runtime.observedAt()).isEqualTo(now);
assertThat(runtime.mode()).isEqualTo("BUSINESS");assertThat(runtime.environment()).isEqualTo("test");assertThat(runtime.continuousEnabled()).isTrue();
assertThat(runtime.intervalMs()).isEqualTo(1200);assertThat(runtime.permitMaxSeconds()).isEqualTo(30);assertThat(runtime.pendingMaxSeconds()).isEqualTo(120);
assertThat(runtime.executionEnabled()).isTrue();assertThat(runtime.grantPresent()).isTrue();
assertThat(runtime.exclusiveHandoffVerified()&&runtime.priorEgressClosed()&&runtime.inflightDrained()&&runtime.transferredBudgetVerified()).isTrue();
var mapper=new com.fasterxml.jackson.databind.ObjectMapper().findAndRegisterModules().disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String serialized=mapper.writeValueAsString(response);var data=mapper.readTree(serialized).path("data");
var names=new java.util.HashSet<String>();data.fieldNames().forEachRemaining(names::add);
assertThat(names).containsExactlyInAnyOrder("schemaVersion","service","observedAt","mode","environment","continuousEnabled",
"sourceAccountFingerprint","grantPresent","grantFingerprint","intervalMs","permitMaxSeconds","executionEnabled","pendingMaxSeconds",
"exclusiveHandoffVerified","priorEgressClosed","inflightDrained","transferredBudgetVerified");
assertThat(serialized).doesNotContain("synthetic-private-account","synthetic-private-grant","synthetic-private-token","synthetic-private-host");
config.setCollectionMode("VALIDATION");config.setContinuousBusinessCollectionEnabled(false);config.setBusinessMinRequestIntervalMs(5000);config.setEgressLeaseSeconds(0);
var changed=controller.runtime("synthetic").data();assertThat(changed.mode()).isEqualTo("VALIDATION");assertThat(changed.continuousEnabled()).isFalse();
assertThat(changed.intervalMs()).isEqualTo(5000);assertThat(changed.pendingMaxSeconds()).isEqualTo(1);
assertThat(runtime.mode()).isEqualTo("BUSINESS");verifyNoInteractions(storage);
}
@Test void runtimeFingerprintsUseRawUtf8WithSeparateDomainsAndExplicitNulls() throws Exception {
var storage=mock(SelfopEgressMapper.class);var config=new com.baiyue.lastleg.selfoperated.analytics.model.SelfopAnalyticsProperties();
var service=new SelfopEgressService(storage,config);
String value=" fixture-账户 ";config.setSourceAccount(value);config.setExclusiveGrantId(value);
var digest=java.security.MessageDigest.getInstance("SHA-256");var hex=java.util.HexFormat.of();
var runtime=service.runtime();
assertThat(runtime.sourceAccountFingerprint()).isEqualTo(hex.formatHex(digest.digest(("selfop-runtime:v1:source-account\n"+value).getBytes(java.nio.charset.StandardCharsets.UTF_8))));
assertThat(runtime.grantFingerprint()).isEqualTo(hex.formatHex(digest.digest(("selfop-runtime:v1:grant\n"+value).getBytes(java.nio.charset.StandardCharsets.UTF_8))));
assertThat(runtime.sourceAccountFingerprint()).isNotEqualTo(runtime.grantFingerprint());
config.setExclusiveGrantId(value.trim());assertThat(service.runtime().grantFingerprint()).isNotEqualTo(runtime.grantFingerprint());
var mapper=new com.fasterxml.jackson.databind.ObjectMapper().findAndRegisterModules();
for(String empty:new String[]{null,""," \t"}){
config.setSourceAccount(empty);config.setExclusiveGrantId(empty);var absent=service.runtime();
assertThat(absent.grantPresent()).isFalse();assertThat(absent.grantFingerprint()).isNull();assertThat(absent.sourceAccountFingerprint()).isNull();
var node=mapper.valueToTree(absent);assertThat(node.has("grantFingerprint")).isTrue();assertThat(node.path("grantFingerprint").isNull()).isTrue();
}
verifyNoInteractions(storage);
}
@Test void invalidInternalTokenCannotReachAuthority(){
var service=mock(SelfopEgressService.class);var c=new SelfopEgressController(new InternalServiceTokenGuard("synthetic"),service);
assertThatThrownBy(()->c.authorize("other",null)).isInstanceOf(org.springframework.web.server.ResponseStatusException.class).satisfies(e->assertThat(((org.springframework.web.server.ResponseStatusException)e).getStatusCode().value()).isEqualTo(401));