mirror of https://github.com/jetkvm/kvm.git
chore: use json schema for ota test data
This commit is contained in:
parent
1880d5bfbc
commit
3b0efa7d20
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -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"`
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
if asserts.skip {
|
} else {
|
||||||
return info
|
t.Fatalf("failed to get update status: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
2
ota.go
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue