chore: use json schema for ota test data

This commit is contained in:
Siyuan 2025-11-17 12:22:57 +00:00
parent 1880d5bfbc
commit 3b0efa7d20
14 changed files with 606 additions and 127 deletions

10
.vscode/settings.json vendored
View File

@ -11,5 +11,13 @@
}, },
"git.ignoreLimitWarning": true, "git.ignoreLimitWarning": true,
"cmake.sourceDirectory": "/workspaces/kvm-static-ip/internal/native/cgo", "cmake.sourceDirectory": "/workspaces/kvm-static-ip/internal/native/cgo",
"cmake.ignoreCMakeListsMissing": true "cmake.ignoreCMakeListsMissing": true,
"json.schemas": [
{
"fileMatch": [
"/internal/ota/testdata/ota/*.json"
],
"url": "./internal/ota/testdata/ota.schema.json"
}
]
} }

View File

@ -12,6 +12,11 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// HttpClient is the interface for the HTTP client
type HttpClient interface {
Do(req *http.Request) (*http.Response, error)
}
// UpdateReleaseAPIEndpoint updates the release API endpoint // UpdateReleaseAPIEndpoint updates the release API endpoint
func (s *State) UpdateReleaseAPIEndpoint(endpoint string) { func (s *State) UpdateReleaseAPIEndpoint(endpoint string) {
s.releaseAPIEndpoint = endpoint s.releaseAPIEndpoint = endpoint
@ -215,7 +220,7 @@ func (s *State) doUpdate(ctx context.Context, params UpdateParams) error {
// UpdateParams represents the parameters for the update // UpdateParams represents the parameters for the update
type UpdateParams struct { type UpdateParams struct {
DeviceID string `json:"deviceID"` DeviceID string `json:"deviceID"`
Components map[string]string `json:"components,omitempty"` Components map[string]string `json:"components"`
IncludePreRelease bool `json:"includePreRelease"` IncludePreRelease bool `json:"includePreRelease"`
CheckOnly bool `json:"checkOnly"` CheckOnly bool `json:"checkOnly"`
ResetConfig bool `json:"resetConfig"` ResetConfig bool `json:"resetConfig"`

View File

@ -1,12 +1,18 @@
package ota package ota
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"embed"
"encoding/json"
"fmt" "fmt"
"io"
"net/http" "net/http"
"net/url"
"os" "os"
"path/filepath"
"testing" "testing"
"time" "time"
@ -16,34 +22,173 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
const pseudoDeviceID = "golang-test" //go:embed testdata/ota
var testDataFS embed.FS
type otaTestStateParams struct { const pseudoDeviceID = "golang-test"
LocalSystemVersion string const releaseAPIEndpoint = "https://api.jetkvm.com/releases"
LocalAppVersion string
WithoutCerts bool type testData struct {
Name string `json:"name"`
WithoutCerts bool `json:"withoutCerts"`
RemoteMetadata []struct {
Code int `json:"code"`
Params map[string]string `json:"params"`
Data UpdateMetadata `json:"data"`
} `json:"remoteMetadata"`
LocalMetadata struct {
SystemVersion string `json:"systemVersion"`
AppVersion string `json:"appVersion"`
} `json:"localMetadata"`
UpdateParams UpdateParams `json:"updateParams"`
Expected struct {
System bool `json:"system"`
App bool `json:"app"`
Error string `json:"error,omitempty"`
} `json:"expected"`
} }
func newOtaState(p otaTestStateParams) *State { func (d *testData) ToFixtures(t *testing.T) map[string]mockData {
fixtures := make(map[string]mockData)
for _, resp := range d.RemoteMetadata {
url, err := url.Parse(releaseAPIEndpoint)
if err != nil {
t.Fatalf("failed to parse release API endpoint: %v", err)
}
query := url.Query()
query.Set("deviceId", pseudoDeviceID)
for key, value := range resp.Params {
query.Set(key, value)
}
url.RawQuery = query.Encode()
fixtures[url.String()] = mockData{
Metadata: &resp.Data,
StatusCode: resp.Code,
}
}
return fixtures
}
func (d *testData) ToUpdateParams() UpdateParams {
d.UpdateParams.DeviceID = pseudoDeviceID
return d.UpdateParams
}
func loadTestData(t *testing.T, filename string) *testData {
f, err := testDataFS.ReadFile(filepath.Join("testdata", "ota", filename))
if err != nil {
t.Fatalf("failed to read test data file %s: %v", filename, err)
}
var testData testData
if err := json.Unmarshal(f, &testData); err != nil {
t.Fatalf("failed to unmarshal test data file %s: %v", filename, err)
}
return &testData
}
type mockData struct {
Metadata *UpdateMetadata
StatusCode int
}
type mockHTTPClient struct {
DoFunc func(req *http.Request) (*http.Response, error)
Fixtures map[string]mockData
}
func compareURLs(a *url.URL, b *url.URL) bool {
if a.String() == b.String() {
return true
}
if a.Host != b.Host || a.Scheme != b.Scheme || a.Path != b.Path {
return false
}
// do a quick check to see if the query parameters are the same
queryA := a.Query()
queryB := b.Query()
if len(queryA) != len(queryB) {
return false
}
for key := range queryA {
if queryA.Get(key) != queryB.Get(key) {
return false
}
}
for key := range queryB {
if queryA.Get(key) != queryB.Get(key) {
return false
}
}
return true
}
func (m *mockHTTPClient) getFixture(expectedURL *url.URL) *mockData {
for u, fixture := range m.Fixtures {
fixtureURL, err := url.Parse(u)
if err != nil {
continue
}
if compareURLs(fixtureURL, expectedURL) {
return &fixture
}
}
return nil
}
func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) {
fixture := m.getFixture(req.URL)
if fixture == nil {
return &http.Response{
StatusCode: http.StatusNotFound,
Body: io.NopCloser(bytes.NewBufferString("")),
}, fmt.Errorf("no fixture found for URL: %s", req.URL.String())
}
resp := &http.Response{
StatusCode: fixture.StatusCode,
}
jsonData, err := json.Marshal(fixture.Metadata)
if err != nil {
return nil, fmt.Errorf("error marshalling metadata: %w", err)
}
resp.Body = io.NopCloser(bytes.NewBufferString(string(jsonData)))
return resp, nil
}
func newMockHTTPClient(fixtures map[string]mockData) *mockHTTPClient {
return &mockHTTPClient{
Fixtures: fixtures,
}
}
func newOtaState(d *testData, t *testing.T) *State {
pseudoGetLocalVersion := func() (systemVersion *semver.Version, appVersion *semver.Version, err error) { pseudoGetLocalVersion := func() (systemVersion *semver.Version, appVersion *semver.Version, err error) {
appVersion = semver.MustParse(p.LocalAppVersion) appVersion = semver.MustParse(d.LocalMetadata.AppVersion)
systemVersion = semver.MustParse(p.LocalSystemVersion) systemVersion = semver.MustParse(d.LocalMetadata.SystemVersion)
return systemVersion, appVersion, nil return systemVersion, appVersion, nil
} }
traceLevel := zerolog.InfoLevel traceLevel := zerolog.InfoLevel
if os.Getenv("TEST_LOG_TRACE") == "true" { if os.Getenv("TEST_LOG_TRACE") == "1" {
traceLevel = zerolog.TraceLevel traceLevel = zerolog.TraceLevel
} }
logger := zerolog.New(os.Stdout).Level(traceLevel) logger := zerolog.New(os.Stdout).Level(traceLevel)
otaState := NewState(Options{ otaState := NewState(Options{
SkipConfirmSystem: true, SkipConfirmSystem: true,
Logger: &logger, Logger: &logger,
ReleaseAPIEndpoint: "https://api.jetkvm.com/releases", ReleaseAPIEndpoint: releaseAPIEndpoint,
GetHTTPClient: func() *http.Client { GetHTTPClient: func() HttpClient {
if d.RemoteMetadata != nil {
return newMockHTTPClient(d.ToFixtures(t))
}
transport := http.DefaultTransport.(*http.Transport).Clone() transport := http.DefaultTransport.(*http.Transport).Clone()
if !p.WithoutCerts { if !d.WithoutCerts {
transport.TLSClientConfig = &tls.Config{RootCAs: rootcerts.ServerCertPool()} transport.TLSClientConfig = &tls.Config{RootCAs: rootcerts.ServerCertPool()}
} else { } else {
transport.TLSClientConfig = &tls.Config{RootCAs: x509.NewCertPool()} transport.TLSClientConfig = &tls.Config{RootCAs: x509.NewCertPool()}
@ -62,146 +207,55 @@ func newOtaState(p otaTestStateParams) *State {
return otaState return otaState
} }
func TestCheckUpdateComponentsWithoutCerts(t *testing.T) { func testUsingJson(t *testing.T, filename string) {
otaState := newOtaState(otaTestStateParams{ td := loadTestData(t, filename)
LocalSystemVersion: "0.2.5", otaState := newOtaState(td, t)
LocalAppVersion: "0.4.7", info, err := otaState.GetUpdateStatus(context.Background(), td.ToUpdateParams())
WithoutCerts: true,
})
_, err := otaState.GetUpdateStatus(context.Background(), UpdateParams{})
assert.ErrorContains(t, err, "certificate signed by unknown authority")
}
type updateStatusAsserts struct {
system bool
app bool
skip bool
}
func testGetUpdateStatus(t *testing.T, p otaTestStateParams, u UpdateParams, asserts updateStatusAsserts) *UpdateStatus {
otaState := newOtaState(p)
info, err := otaState.GetUpdateStatus(context.Background(), u)
t.Logf("update status: %+v", info)
if err != nil { if err != nil {
t.Fatalf("failed to check update: %v", err) if td.Expected.Error != "" {
assert.ErrorContains(t, err, td.Expected.Error)
} else {
t.Fatalf("failed to get update status: %v", err)
} }
if asserts.skip {
return info
} }
if asserts.system { if td.Expected.System {
assert.True(t, info.SystemUpdateAvailable, fmt.Sprintf("system update should available, but reason: %s", info.SystemUpdateAvailableReason)) assert.True(t, info.SystemUpdateAvailable, fmt.Sprintf("system update should available, but reason: %s", info.SystemUpdateAvailableReason))
} else { } else {
assert.False(t, info.SystemUpdateAvailable, fmt.Sprintf("system update should not be available, but reason: %s", info.SystemUpdateAvailableReason)) assert.False(t, info.SystemUpdateAvailable, fmt.Sprintf("system update should not be available, but reason: %s", info.SystemUpdateAvailableReason))
} }
if asserts.app {
if td.Expected.App {
assert.True(t, info.AppUpdateAvailable, fmt.Sprintf("app update should available, but reason: %s", info.AppUpdateAvailableReason)) assert.True(t, info.AppUpdateAvailable, fmt.Sprintf("app update should available, but reason: %s", info.AppUpdateAvailableReason))
} else { } else {
assert.False(t, info.AppUpdateAvailable, fmt.Sprintf("app update should not be available, but reason: %s", info.AppUpdateAvailableReason)) assert.False(t, info.AppUpdateAvailable, fmt.Sprintf("app update should not be available, but reason: %s", info.AppUpdateAvailableReason))
} }
return info
} }
func TestCheckUpdateComponentsSystemOnlyUpgrade(t *testing.T) { func TestCheckUpdateComponentsSystemOnlyUpgrade(t *testing.T) {
_ = testGetUpdateStatus(t, otaTestStateParams{ testUsingJson(t, "system_only_upgrade.json")
LocalSystemVersion: "0.2.2", // remote >= 0.2.5
LocalAppVersion: "0.4.5", // remote >= 0.4.7
WithoutCerts: false,
}, UpdateParams{
DeviceID: pseudoDeviceID,
IncludePreRelease: false,
Components: map[string]string{"system": ""},
}, updateStatusAsserts{
system: true,
app: false,
})
} }
func TestCheckUpdateComponentsSystemOnlyDowngrade(t *testing.T) { func TestCheckUpdateComponentsSystemOnlyDowngrade(t *testing.T) {
_ = testGetUpdateStatus(t, otaTestStateParams{ testUsingJson(t, "system_only_downgrade.json")
LocalSystemVersion: "0.2.5", // remote >= 0.2.5
LocalAppVersion: "0.4.5", // remote >= 0.4.7
WithoutCerts: false,
}, UpdateParams{
DeviceID: pseudoDeviceID,
Components: map[string]string{"system": "0.2.2"},
IncludePreRelease: false,
}, updateStatusAsserts{
system: true,
app: false,
})
} }
func TestCheckUpdateComponentsAppOnlyUpgrade(t *testing.T) { func TestCheckUpdateComponentsAppOnlyUpgrade(t *testing.T) {
_ = testGetUpdateStatus(t, otaTestStateParams{ testUsingJson(t, "app_only_upgrade.json")
LocalSystemVersion: "0.2.2", // remote >= 0.2.5
LocalAppVersion: "0.4.5", // remote >= 0.4.7
WithoutCerts: false,
}, UpdateParams{
DeviceID: pseudoDeviceID,
IncludePreRelease: false,
Components: map[string]string{"app": ""},
}, updateStatusAsserts{
system: false,
app: true,
})
} }
func TestCheckUpdateComponentsAppOnlyDowngrade(t *testing.T) { func TestCheckUpdateComponentsAppOnlyDowngrade(t *testing.T) {
_ = testGetUpdateStatus(t, otaTestStateParams{ testUsingJson(t, "app_only_downgrade.json")
LocalSystemVersion: "0.2.2", // remote >= 0.2.5
LocalAppVersion: "0.4.5", // remote >= 0.4.7
WithoutCerts: false,
}, UpdateParams{
DeviceID: pseudoDeviceID,
Components: map[string]string{"app": "0.4.6"},
IncludePreRelease: false,
}, updateStatusAsserts{
system: false,
app: true,
})
} }
func TestCheckUpdateComponentsSystemBothUpgrade(t *testing.T) { func TestCheckUpdateComponentsSystemBothUpgrade(t *testing.T) {
_ = testGetUpdateStatus(t, otaTestStateParams{ testUsingJson(t, "both_upgrade.json")
LocalSystemVersion: "0.2.2", // remote >= 0.2.5
LocalAppVersion: "0.4.5", // remote >= 0.4.7
WithoutCerts: false,
}, UpdateParams{
DeviceID: pseudoDeviceID,
IncludePreRelease: false,
Components: map[string]string{"system": "", "app": ""},
}, updateStatusAsserts{
system: true,
app: true,
})
} }
func TestCheckUpdateComponentsSystemBothDowngrade(t *testing.T) { func TestCheckUpdateComponentsSystemBothDowngrade(t *testing.T) {
_ = testGetUpdateStatus(t, otaTestStateParams{ testUsingJson(t, "both_downgrade.json")
LocalSystemVersion: "0.2.5", // remote >= 0.2.5
LocalAppVersion: "0.4.5", // remote >= 0.4.7
WithoutCerts: false,
}, UpdateParams{
DeviceID: pseudoDeviceID,
Components: map[string]string{"system": "0.2.2", "app": "0.4.6"},
IncludePreRelease: false,
}, updateStatusAsserts{
system: true,
app: true,
})
} }
func TestCheckUpdateComponentsNoComponents(t *testing.T) { func TestCheckUpdateComponentsNoComponents(t *testing.T) {
_ = testGetUpdateStatus(t, otaTestStateParams{ testUsingJson(t, "no_components.json")
LocalSystemVersion: "0.2.2",
LocalAppVersion: "0.4.2",
WithoutCerts: false,
}, UpdateParams{
DeviceID: pseudoDeviceID,
IncludePreRelease: false,
}, updateStatusAsserts{
system: true,
app: true,
})
} }

View File

@ -1,7 +1,6 @@
package ota package ota
import ( import (
"net/http"
"sync" "sync"
"time" "time"
@ -100,7 +99,7 @@ type HwRebootFunc func(force bool, postRebootAction *PostRebootAction, delay tim
type ResetConfigFunc func() error type ResetConfigFunc func() error
// GetHTTPClientFunc is a function that returns the HTTP client // GetHTTPClientFunc is a function that returns the HTTP client
type GetHTTPClientFunc func() *http.Client type GetHTTPClientFunc func() HttpClient
// OnStateUpdateFunc is a function that updates the state of the OTA // OnStateUpdateFunc is a function that updates the state of the OTA
type OnStateUpdateFunc func(state *RPCState) type OnStateUpdateFunc func(state *RPCState)

159
internal/ota/testdata/ota.schema.json vendored Normal file
View File

@ -0,0 +1,159 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "OTA Test Data Schema",
"description": "Schema for OTA update test data",
"type": "object",
"required": ["name", "remoteMetadata", "localMetadata", "updateParams"],
"properties": {
"name": {
"type": "string",
"description": "Name of the test case"
},
"withoutCerts": {
"type": "boolean",
"default": false,
"description": "Whether to run the test without Root CA certificates"
},
"remoteMetadata": {
"type": "array",
"description": "Remote metadata responses",
"items": {
"type": "object",
"required": ["params", "code", "data"],
"properties": {
"params": {
"type": "object",
"description": "Query parameters used for the request",
"required": ["prerelease"],
"properties": {
"prerelease": {
"type": "string",
"description": "Whether to include pre-release versions"
},
"appVersion": {
"type": "string",
"description": "Application version string",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"systemVersion": {
"type": "string",
"description": "System version string",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
}
},
"additionalProperties": false
},
"code": {
"type": "integer",
"description": "HTTP status code"
},
"data": {
"type": "object",
"required": ["appVersion", "appUrl", "appHash", "systemVersion", "systemUrl", "systemHash"],
"properties": {
"appVersion": {
"type": "string",
"description": "Application version string",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"appUrl": {
"type": "string",
"description": "URL to download the application",
"format": "uri"
},
"appHash": {
"type": "string",
"description": "SHA-256 hash of the application",
"pattern": "^[a-f0-9]{64}$"
},
"systemVersion": {
"type": "string",
"description": "System version string",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"systemUrl": {
"type": "string",
"description": "URL to download the system",
"format": "uri"
},
"systemHash": {
"type": "string",
"description": "SHA-256 hash of the system",
"pattern": "^[a-f0-9]{64}$"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"localMetadata": {
"type": "object",
"description": "Local metadata containing current installed versions",
"required": ["systemVersion", "appVersion"],
"properties": {
"systemVersion": {
"type": "string",
"description": "Currently installed system version",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"appVersion": {
"type": "string",
"description": "Currently installed application version",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
}
},
"additionalProperties": false
},
"updateParams": {
"type": "object",
"description": "Parameters for the update operation",
"required": ["includePreRelease"],
"properties": {
"includePreRelease": {
"type": "boolean",
"description": "Whether to include pre-release versions"
},
"components": {
"type": "object",
"description": "Component update configuration",
"properties": {
"system": {
"type": "string",
"description": "System component update configuration (empty string to update)"
},
"app": {
"type": "string",
"description": "App component update configuration (version string to update to)"
}
},
"additionalProperties": true
}
},
"additionalProperties": false
},
"expected": {
"type": "object",
"description": "Expected update results",
"required": [],
"properties": {
"system": {
"type": "boolean",
"description": "Whether system update is expected"
},
"app": {
"type": "boolean",
"description": "Whether app update is expected"
},
"error": {
"type": "string",
"description": "Error message if the test case is expected to fail"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,34 @@
{
"name": "Downgrade App Only",
"remoteMetadata": [
{
"params": {
"prerelease": "false",
"appVersion": "0.4.6"
},
"code": 200,
"data": {
"appVersion": "0.4.6",
"appUrl": "https://update.jetkvm.com/app/0.4.6/jetkvm_app",
"appHash": "714f33432f17035e38d238bf376e98f3073e6cc2845d269ff617503d12d92bdd",
"systemVersion": "0.2.5",
"systemUrl": "https://update.jetkvm.com/system/0.2.5/system.tar",
"systemHash": "2323463ea8652be767d94514e548f90dd61b1ebcc0fb1834d700fac5b3d88a35"
}
}
],
"localMetadata": {
"systemVersion": "0.2.2",
"appVersion": "0.4.5"
},
"updateParams": {
"includePreRelease": false,
"components": {
"app": "0.4.6"
}
},
"expected": {
"system": false,
"app": true
}
}

View File

@ -0,0 +1,33 @@
{
"name": "Upgrade App Only",
"remoteMetadata": [
{
"params": {
"prerelease": "false"
},
"code": 200,
"data": {
"appVersion": "0.4.7",
"appUrl": "https://update.jetkvm.com/app/0.4.7/jetkvm_app",
"appHash": "714f33432f17035e38d238bf376e98f3073e6cc2845d269ff617503d12d92bdd",
"systemVersion": "0.2.5",
"systemUrl": "https://update.jetkvm.com/system/0.2.5/system.tar",
"systemHash": "2323463ea8652be767d94514e548f90dd61b1ebcc0fb1834d700fac5b3d88a35"
}
}
],
"localMetadata": {
"systemVersion": "0.2.2",
"appVersion": "0.4.5"
},
"updateParams": {
"includePreRelease": false,
"components": {
"app": ""
}
},
"expected": {
"system": false,
"app": true
}
}

View File

@ -0,0 +1,37 @@
{
"name": "Downgrade System & App",
"remoteMetadata": [
{
"params": {
"prerelease": "false",
"systemVersion": "0.2.2",
"appVersion": "0.4.6"
},
"code": 200,
"data": {
"appVersion": "0.4.6",
"appUrl": "https://update.jetkvm.com/app/0.4.6/jetkvm_app",
"appHash": "714f33432f17035e38d238bf376e98f3073e6cc2845d269ff617503d12d92bdd",
"systemVersion": "0.2.2",
"systemUrl": "https://update.jetkvm.com/system/0.2.2/system.tar",
"systemHash": "2323463ea8652be767d94514e548f90dd61b1ebcc0fb1834d700fac5b3d88a35"
}
}
],
"localMetadata": {
"systemVersion": "0.2.5",
"appVersion": "0.4.5"
},
"updateParams": {
"includePreRelease": false,
"components": {
"system": "0.2.2",
"app": "0.4.6"
}
},
"expected": {
"system": true,
"app": true
}
}

View File

@ -0,0 +1,34 @@
{
"name": "Upgrade System & App (components given)",
"remoteMetadata": [
{
"params": {
"prerelease": "false"
},
"code": 200,
"data": {
"appVersion": "0.4.7",
"appUrl": "https://update.jetkvm.com/app/0.4.7/jetkvm_app",
"appHash": "714f33432f17035e38d238bf376e98f3073e6cc2845d269ff617503d12d92bdd",
"systemVersion": "0.2.5",
"systemUrl": "https://update.jetkvm.com/system/0.2.5/system.tar",
"systemHash": "2323463ea8652be767d94514e548f90dd61b1ebcc0fb1834d700fac5b3d88a35"
}
}
],
"localMetadata": {
"systemVersion": "0.2.2",
"appVersion": "0.4.5"
},
"updateParams": {
"includePreRelease": false,
"components": {
"system": "",
"app": ""
}
},
"expected": {
"system": true,
"app": true
}
}

View File

@ -0,0 +1,32 @@
{
"name": "Upgrade System & App (no components given)",
"remoteMetadata": [
{
"params": {
"prerelease": "false"
},
"code": 200,
"data": {
"appVersion": "0.4.7",
"appUrl": "https://update.jetkvm.com/app/0.4.7/jetkvm_app",
"appHash": "714f33432f17035e38d238bf376e98f3073e6cc2845d269ff617503d12d92bdd",
"systemVersion": "0.2.5",
"systemUrl": "https://update.jetkvm.com/system/0.2.5/system.tar",
"systemHash": "2323463ea8652be767d94514e548f90dd61b1ebcc0fb1834d700fac5b3d88a35"
}
}
],
"localMetadata": {
"systemVersion": "0.2.2",
"appVersion": "0.4.2"
},
"updateParams": {
"includePreRelease": false,
"components": {}
},
"expected": {
"system": true,
"app": true
}
}

View File

@ -0,0 +1,34 @@
{
"name": "Downgrade System Only",
"remoteMetadata": [
{
"params": {
"prerelease": "false",
"systemVersion": "0.2.2"
},
"code": 200,
"data": {
"appVersion": "0.4.7",
"appUrl": "https://update.jetkvm.com/app/0.4.7/jetkvm_app",
"appHash": "714f33432f17035e38d238bf376e98f3073e6cc2845d269ff617503d12d92bdd",
"systemVersion": "0.2.2",
"systemUrl": "https://update.jetkvm.com/system/0.2.2/system.tar",
"systemHash": "2323463ea8652be767d94514e548f90dd61b1ebcc0fb1834d700fac5b3d88a35"
}
}
],
"localMetadata": {
"systemVersion": "0.2.5",
"appVersion": "0.4.5"
},
"updateParams": {
"includePreRelease": false,
"components": {
"system": "0.2.2"
}
},
"expected": {
"system": true,
"app": false
}
}

View File

@ -0,0 +1,33 @@
{
"name": "Upgrade System Only",
"remoteMetadata": [
{
"params": {
"prerelease": "false"
},
"code": 200,
"data": {
"appVersion": "0.4.7",
"appUrl": "https://update.jetkvm.com/app/0.4.7/jetkvm_app",
"appHash": "714f33432f17035e38d238bf376e98f3073e6cc2845d269ff617503d12d92bdd",
"systemVersion": "0.2.6",
"systemUrl": "https://update.jetkvm.com/system/0.2.6/system.tar",
"systemHash": "2323463ea8652be767d94514e548f90dd61b1ebcc0fb1834d700fac5b3d88a35"
}
}
],
"localMetadata": {
"systemVersion": "0.2.5",
"appVersion": "0.4.5"
},
"updateParams": {
"includePreRelease": false,
"components": {
"system": ""
}
},
"expected": {
"system": true,
"app": false
}
}

View File

@ -0,0 +1,17 @@
{
"name": "Without Certs",
"localMetadata": {
"systemVersion": "0.2.5",
"appVersion": "0.4.7"
},
"updateParams": {
"includePreRelease": false,
"components": {}
},
"expected": {
"system": false,
"app": false,
"error": "certificate signed by unknown authority"
}
}

2
ota.go
View File

@ -19,7 +19,7 @@ func initOta() {
otaState = ota.NewState(ota.Options{ otaState = ota.NewState(ota.Options{
Logger: otaLogger, Logger: otaLogger,
ReleaseAPIEndpoint: config.GetUpdateAPIURL(), ReleaseAPIEndpoint: config.GetUpdateAPIURL(),
GetHTTPClient: func() *http.Client { GetHTTPClient: func() ota.HttpClient {
transport := http.DefaultTransport.(*http.Transport).Clone() transport := http.DefaultTransport.(*http.Transport).Clone()
transport.Proxy = config.NetworkConfig.GetTransportProxyFunc() transport.Proxy = config.NetworkConfig.GetTransportProxyFunc()