feat: add signed PostgreSQL release updates
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package com.kaidi.oa.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.kaidi.oa.common.ApiException;
|
||||
import com.kaidi.oa.config.UpdateProperties;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.info.BuildProperties;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class SystemUpdateServiceTest {
|
||||
|
||||
@Test
|
||||
void comparesReleaseVersionsWithoutLexicographicMistakes() {
|
||||
assertThat(SystemUpdateService.compareVersions("v0.10.0", "0.9.9")).isPositive();
|
||||
assertThat(SystemUpdateService.compareVersions("1.0.0", "1.0.0-rc.1")).isPositive();
|
||||
assertThat(SystemUpdateService.compareVersions("1.2", "1.2.0")).isZero();
|
||||
assertThat(SystemUpdateService.compareVersions("1.0.0+build.2", "1.0.0+build.1")).isZero();
|
||||
assertThat(SystemUpdateService.compareVersions("1.0.0-alpha.10", "1.0.0-alpha.2")).isPositive();
|
||||
assertThat(SystemUpdateService.compareVersions("1.0.0-alpha.1", "1.0.0-alpha.beta")).isNegative();
|
||||
assertThat(SystemUpdateService.compareVersions("999999999999999999999.0.0", "2.0.0")).isPositive();
|
||||
assertThat(SystemUpdateService.compareVersions("1.0.0", "dev")).isPositive();
|
||||
}
|
||||
|
||||
@Test
|
||||
void checksLatestGiteaReleaseAndRequiresSignedAssetSet() throws Exception {
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
|
||||
server.createContext("/api/v1/repos/awaioi/ERP/releases/latest", exchange -> {
|
||||
byte[] body = """
|
||||
{
|
||||
"tag_name": "v0.2.0",
|
||||
"body": "PostgreSQL production release",
|
||||
"draft": false,
|
||||
"prerelease": false,
|
||||
"published_at": "2026-08-03T10:00:00Z",
|
||||
"assets": [
|
||||
{"name":"kaidi-erp-0.2.0.tar.gz","browser_download_url":"https://example.test/app","size":123},
|
||||
{"name":"SHA256SUMS","browser_download_url":"https://example.test/sums","size":64},
|
||||
{"name":"SHA256SUMS.sig","browser_download_url":"https://example.test/sig","size":64}
|
||||
]
|
||||
}
|
||||
""".getBytes(StandardCharsets.UTF_8);
|
||||
exchange.getResponseHeaders().add("Content-Type", "application/json");
|
||||
exchange.sendResponseHeaders(200, body.length);
|
||||
exchange.getResponseBody().write(body);
|
||||
exchange.close();
|
||||
});
|
||||
server.start();
|
||||
try {
|
||||
UpdateProperties properties = configuredProperties(server.getAddress().getPort());
|
||||
SystemUpdateService service = service(properties, "0.1.0");
|
||||
|
||||
SystemUpdateService.UpdateStatus status = service.check();
|
||||
|
||||
assertThat(status.updateAvailable()).isTrue();
|
||||
assertThat(status.currentVersion()).isEqualTo("0.1.0");
|
||||
assertThat(status.latestVersion()).isEqualTo("0.2.0");
|
||||
assertThat(status.assets()).extracting(SystemUpdateService.ReleaseAsset::name)
|
||||
.containsExactly("kaidi-erp-0.2.0.tar.gz", "SHA256SUMS", "SHA256SUMS.sig");
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsPlainHttpUnlessDevelopmentOverrideIsExplicit() {
|
||||
UpdateProperties properties = configuredProperties(1);
|
||||
properties.setAllowInsecureHttp(false);
|
||||
SystemUpdateService service = service(properties, "0.1.0");
|
||||
|
||||
assertThatThrownBy(service::check)
|
||||
.isInstanceOf(ApiException.class)
|
||||
.hasMessageContaining("HTTPS");
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistedHelperStateComputesAvailabilityFromVersionAndKeepsTimestamp() throws Exception {
|
||||
Path stateFile = Files.createTempFile("system-update-state-", ".json");
|
||||
try {
|
||||
UpdateProperties properties = configuredProperties(1);
|
||||
properties.setStateFile(stateFile.toString());
|
||||
SystemUpdateService service = service(properties, "0.2.0");
|
||||
|
||||
Files.writeString(stateFile, """
|
||||
{"phase":"FAILED","progress":0,"message":"update failed",\
|
||||
"version":"0.2.0","updatedAt":"2026-08-03T12:34:56Z"}
|
||||
""");
|
||||
SystemUpdateService.UpdateStatus failed = service.status();
|
||||
|
||||
assertThat(failed.updateAvailable()).isFalse();
|
||||
assertThat(failed.checkedAt()).isEqualTo(Instant.parse("2026-08-03T12:34:56Z"));
|
||||
|
||||
Files.writeString(stateFile, """
|
||||
{"phase":"ROLLED_BACK","progress":100,"message":"rolled back",\
|
||||
"version":"0.3.0","updatedAt":"2026-08-03T12:35:56Z"}
|
||||
""");
|
||||
assertThat(service.status().updateAvailable()).isTrue();
|
||||
} finally {
|
||||
Files.deleteIfExists(stateFile);
|
||||
}
|
||||
}
|
||||
|
||||
private static UpdateProperties configuredProperties(int port) {
|
||||
UpdateProperties properties = new UpdateProperties();
|
||||
properties.setEnabled(true);
|
||||
properties.setAllowInsecureHttp(true);
|
||||
properties.setGiteaBaseUrl("http://127.0.0.1:" + port);
|
||||
properties.setRepository("awaioi/ERP");
|
||||
return properties;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static SystemUpdateService service(UpdateProperties properties, String version) {
|
||||
Properties buildValues = new Properties();
|
||||
buildValues.setProperty("version", version);
|
||||
ObjectProvider<BuildProperties> provider = mock(ObjectProvider.class);
|
||||
when(provider.getIfAvailable()).thenReturn(new BuildProperties(buildValues));
|
||||
return new SystemUpdateService(properties, new ObjectMapper(), provider);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user