feat(config): 添加配置管理功能
This commit is contained in:
@@ -15,17 +15,17 @@ type MirrorResponse struct {
|
|||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Msg string `json:"msg"`
|
Msg string `json:"msg"`
|
||||||
Data struct {
|
Data struct {
|
||||||
VersionName string `json:"version_name"`
|
VersionName string `json:"version_name"`
|
||||||
VersionNumber int `json:"version_number"`
|
VersionNumber int `json:"version_number"`
|
||||||
URL string `json:"url,omitempty"` // Only present when using CDK
|
URL string `json:"url,omitempty"` // Only present when using CDK
|
||||||
SHA256 string `json:"sha256,omitempty"` // Only present when using CDK
|
SHA256 string `json:"sha256,omitempty"` // Only present when using CDK
|
||||||
Channel string `json:"channel"`
|
Channel string `json:"channel"`
|
||||||
OS string `json:"os"`
|
OS string `json:"os"`
|
||||||
Arch string `json:"arch"`
|
Arch string `json:"arch"`
|
||||||
UpdateType string `json:"update_type,omitempty"` // Only present when using CDK
|
UpdateType string `json:"update_type,omitempty"` // Only present when using CDK
|
||||||
ReleaseNote string `json:"release_note"`
|
ReleaseNote string `json:"release_note"`
|
||||||
FileSize int64 `json:"filesize,omitempty"` // Only present when using CDK
|
FileSize int64 `json:"filesize,omitempty"` // Only present when using CDK
|
||||||
CDKExpiredTime int64 `json:"cdk_expired_time,omitempty"` // Only present when using CDK
|
CDKExpiredTime int64 `json:"cdk_expired_time,omitempty"` // Only present when using CDK
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,62 +66,62 @@ func NewClient() *Client {
|
|||||||
func (c *Client) CheckUpdate(params UpdateCheckParams) (*MirrorResponse, error) {
|
func (c *Client) CheckUpdate(params UpdateCheckParams) (*MirrorResponse, error) {
|
||||||
// Construct the API URL
|
// Construct the API URL
|
||||||
apiURL := fmt.Sprintf("%s/%s/latest", c.baseURL, params.ResourceID)
|
apiURL := fmt.Sprintf("%s/%s/latest", c.baseURL, params.ResourceID)
|
||||||
|
|
||||||
// Parse URL to add query parameters
|
// Parse URL to add query parameters
|
||||||
u, err := url.Parse(apiURL)
|
u, err := url.Parse(apiURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse API URL: %w", err)
|
return nil, fmt.Errorf("failed to parse API URL: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add query parameters
|
// Add query parameters
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
q.Set("current_version", params.CurrentVersion)
|
q.Set("current_version", params.CurrentVersion)
|
||||||
q.Set("channel", params.Channel)
|
q.Set("channel", params.Channel)
|
||||||
q.Set("os", "") // Empty for cross-platform
|
q.Set("os", "") // Empty for cross-platform
|
||||||
q.Set("arch", "") // Empty for cross-platform
|
q.Set("arch", "") // Empty for cross-platform
|
||||||
|
|
||||||
if params.CDK != "" {
|
if params.CDK != "" {
|
||||||
q.Set("cdk", params.CDK)
|
q.Set("cdk", params.CDK)
|
||||||
}
|
}
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
// Create HTTP request
|
// Create HTTP request
|
||||||
req, err := http.NewRequest("GET", u.String(), nil)
|
req, err := http.NewRequest("GET", u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
|
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set User-Agent header
|
// Set User-Agent header
|
||||||
if params.UserAgent != "" {
|
if params.UserAgent != "" {
|
||||||
req.Header.Set("User-Agent", params.UserAgent)
|
req.Header.Set("User-Agent", params.UserAgent)
|
||||||
} else {
|
} else {
|
||||||
req.Header.Set("User-Agent", "LightweightUpdater/1.0")
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make HTTP request
|
// Make HTTP request
|
||||||
resp, err := c.httpClient.Do(req)
|
resp, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to make HTTP request: %w", err)
|
return nil, fmt.Errorf("failed to make HTTP request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// Check HTTP status code
|
// Check HTTP status code
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, fmt.Errorf("API returned non-200 status code: %d", resp.StatusCode)
|
return nil, fmt.Errorf("API returned non-200 status code: %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read response body
|
// Read response body
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse JSON response
|
// Parse JSON response
|
||||||
var mirrorResp MirrorResponse
|
var mirrorResp MirrorResponse
|
||||||
if err := json.Unmarshal(body, &mirrorResp); err != nil {
|
if err := json.Unmarshal(body, &mirrorResp); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse JSON response: %w", err)
|
return nil, fmt.Errorf("failed to parse JSON response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &mirrorResp, nil
|
return &mirrorResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,13 +129,13 @@ func (c *Client) CheckUpdate(params UpdateCheckParams) (*MirrorResponse, error)
|
|||||||
func (c *Client) CheckUpdateLegacy(resourceID, currentVersion, cdk, userAgent string) (*MirrorResponse, error) {
|
func (c *Client) CheckUpdateLegacy(resourceID, currentVersion, cdk, userAgent string) (*MirrorResponse, error) {
|
||||||
// Construct the API URL
|
// Construct the API URL
|
||||||
apiURL := fmt.Sprintf("%s/%s/latest", c.baseURL, resourceID)
|
apiURL := fmt.Sprintf("%s/%s/latest", c.baseURL, resourceID)
|
||||||
|
|
||||||
// Parse URL to add query parameters
|
// Parse URL to add query parameters
|
||||||
u, err := url.Parse(apiURL)
|
u, err := url.Parse(apiURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse API URL: %w", err)
|
return nil, fmt.Errorf("failed to parse API URL: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add query parameters
|
// Add query parameters
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
q.Set("current_version", currentVersion)
|
q.Set("current_version", currentVersion)
|
||||||
@@ -143,44 +143,44 @@ func (c *Client) CheckUpdateLegacy(resourceID, currentVersion, cdk, userAgent st
|
|||||||
q.Set("cdk", cdk)
|
q.Set("cdk", cdk)
|
||||||
}
|
}
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
// Create HTTP request
|
// Create HTTP request
|
||||||
req, err := http.NewRequest("GET", u.String(), nil)
|
req, err := http.NewRequest("GET", u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
|
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set User-Agent header
|
// Set User-Agent header
|
||||||
if userAgent != "" {
|
if userAgent != "" {
|
||||||
req.Header.Set("User-Agent", userAgent)
|
req.Header.Set("User-Agent", userAgent)
|
||||||
} else {
|
} else {
|
||||||
req.Header.Set("User-Agent", "LightweightUpdater/1.0")
|
req.Header.Set("User-Agent", "LightweightUpdater/1.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make HTTP request
|
// Make HTTP request
|
||||||
resp, err := c.httpClient.Do(req)
|
resp, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to make HTTP request: %w", err)
|
return nil, fmt.Errorf("failed to make HTTP request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// Check HTTP status code
|
// Check HTTP status code
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, fmt.Errorf("API returned non-200 status code: %d", resp.StatusCode)
|
return nil, fmt.Errorf("API returned non-200 status code: %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read response body
|
// Read response body
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse JSON response
|
// Parse JSON response
|
||||||
var mirrorResp MirrorResponse
|
var mirrorResp MirrorResponse
|
||||||
if err := json.Unmarshal(body, &mirrorResp); err != nil {
|
if err := json.Unmarshal(body, &mirrorResp); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse JSON response: %w", err)
|
return nil, fmt.Errorf("failed to parse JSON response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &mirrorResp, nil
|
return &mirrorResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,17 +190,17 @@ func (c *Client) IsUpdateAvailable(response *MirrorResponse, currentVersion stri
|
|||||||
if response.Code != 0 {
|
if response.Code != 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get latest version from response
|
// Get latest version from response
|
||||||
latestVersion := response.Data.VersionName
|
latestVersion := response.Data.VersionName
|
||||||
if latestVersion == "" {
|
if latestVersion == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert version formats for comparison
|
// Convert version formats for comparison
|
||||||
currentVersionNormalized := c.normalizeVersionForComparison(currentVersion)
|
currentVersionNormalized := c.normalizeVersionForComparison(currentVersion)
|
||||||
latestVersionNormalized := c.normalizeVersionForComparison(latestVersion)
|
latestVersionNormalized := c.normalizeVersionForComparison(latestVersion)
|
||||||
|
|
||||||
// Compare versions using semantic version comparison
|
// Compare versions using semantic version comparison
|
||||||
return compareVersions(currentVersionNormalized, latestVersionNormalized) < 0
|
return compareVersions(currentVersionNormalized, latestVersionNormalized) < 0
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ func (c *Client) normalizeVersionForComparison(version string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return as-is if already in standard format
|
// Return as-is if already in standard format
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
@@ -230,17 +230,17 @@ func compareVersions(v1, v2 string) int {
|
|||||||
// Normalize versions by removing 'v' prefix if present
|
// Normalize versions by removing 'v' prefix if present
|
||||||
v1 = normalizeVersion(v1)
|
v1 = normalizeVersion(v1)
|
||||||
v2 = normalizeVersion(v2)
|
v2 = normalizeVersion(v2)
|
||||||
|
|
||||||
// Parse version components
|
// Parse version components
|
||||||
parts1 := parseVersionParts(v1)
|
parts1 := parseVersionParts(v1)
|
||||||
parts2 := parseVersionParts(v2)
|
parts2 := parseVersionParts(v2)
|
||||||
|
|
||||||
// Compare each component
|
// Compare each component
|
||||||
maxLen := len(parts1)
|
maxLen := len(parts1)
|
||||||
if len(parts2) > maxLen {
|
if len(parts2) > maxLen {
|
||||||
maxLen = len(parts2)
|
maxLen = len(parts2)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < maxLen; i++ {
|
for i := 0; i < maxLen; i++ {
|
||||||
var p1, p2 int
|
var p1, p2 int
|
||||||
if i < len(parts1) {
|
if i < len(parts1) {
|
||||||
@@ -249,14 +249,14 @@ func compareVersions(v1, v2 string) int {
|
|||||||
if i < len(parts2) {
|
if i < len(parts2) {
|
||||||
p2 = parts2[i]
|
p2 = parts2[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
if p1 < p2 {
|
if p1 < p2 {
|
||||||
return -1
|
return -1
|
||||||
} else if p1 > p2 {
|
} else if p1 > p2 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,10 +273,10 @@ func parseVersionParts(version string) []int {
|
|||||||
if version == "" {
|
if version == "" {
|
||||||
return []int{0}
|
return []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := make([]int, 0, 3)
|
parts := make([]int, 0, 3)
|
||||||
current := 0
|
current := 0
|
||||||
|
|
||||||
for _, char := range version {
|
for _, char := range version {
|
||||||
if char >= '0' && char <= '9' {
|
if char >= '0' && char <= '9' {
|
||||||
current = current*10 + int(char-'0')
|
current = current*10 + int(char-'0')
|
||||||
@@ -288,15 +288,15 @@ func parseVersionParts(version string) []int {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the last component
|
// Add the last component
|
||||||
parts = append(parts, current)
|
parts = append(parts, current)
|
||||||
|
|
||||||
// Ensure at least 3 components (major.minor.patch)
|
// Ensure at least 3 components (major.minor.patch)
|
||||||
for len(parts) < 3 {
|
for len(parts) < 3 {
|
||||||
parts = append(parts, 0)
|
parts = append(parts, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts
|
return parts
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,17 +304,17 @@ func parseVersionParts(version string) []int {
|
|||||||
func (c *Client) GetOfficialDownloadURL(versionName string) string {
|
func (c *Client) GetOfficialDownloadURL(versionName string) string {
|
||||||
// Official download site base URL
|
// Official download site base URL
|
||||||
baseURL := "http://221.236.27.82:10197/d/AUTO_MAA"
|
baseURL := "http://221.236.27.82:10197/d/AUTO_MAA"
|
||||||
|
|
||||||
// Convert version name to filename format
|
// Convert version name to filename format
|
||||||
// e.g., "v4.4.0" -> "AUTO_MAA_v4.4.0.zip"
|
// e.g., "v4.4.0" -> "AUTO_MAA_v4.4.0.zip"
|
||||||
// e.g., "v4.4.1-beta3" -> "AUTO_MAA_v4.4.1-beta.3.zip"
|
// e.g., "v4.4.1-beta3" -> "AUTO_MAA_v4.4.1-beta.3.zip"
|
||||||
filename := fmt.Sprintf("AUTO_MAA_%s.zip", versionName)
|
filename := fmt.Sprintf("AUTO_MAA_%s.zip", versionName)
|
||||||
|
|
||||||
// Handle beta versions: convert "beta3" to "beta.3"
|
// Handle beta versions: convert "beta3" to "beta.3"
|
||||||
if strings.Contains(filename, "-beta") && !strings.Contains(filename, "-beta.") {
|
if strings.Contains(filename, "-beta") && !strings.Contains(filename, "-beta.") {
|
||||||
filename = strings.Replace(filename, "-beta", "-beta.", 1)
|
filename = strings.Replace(filename, "-beta", "-beta.", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s/%s", baseURL, filename)
|
return fmt.Sprintf("%s/%s", baseURL, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,4 +329,4 @@ func (c *Client) GetDownloadURL(response *MirrorResponse) string {
|
|||||||
return response.Data.URL
|
return response.Data.URL
|
||||||
}
|
}
|
||||||
return c.GetOfficialDownloadURL(response.Data.VersionName)
|
return c.GetOfficialDownloadURL(response.Data.VersionName)
|
||||||
}
|
}
|
||||||
|
|||||||
248
Go_Updater/config/config.go
Normal file
248
Go_Updater/config/config.go
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"lightweight-updater/assets"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config represents the application configuration
|
||||||
|
type Config struct {
|
||||||
|
ResourceID string `yaml:"resource_id"`
|
||||||
|
CurrentVersion string `yaml:"current_version"`
|
||||||
|
CDK string `yaml:"cdk,omitempty"`
|
||||||
|
UserAgent string `yaml:"user_agent"`
|
||||||
|
BackupURL string `yaml:"backup_url"`
|
||||||
|
LogLevel string `yaml:"log_level"`
|
||||||
|
AutoCheck bool `yaml:"auto_check"`
|
||||||
|
CheckInterval int `yaml:"check_interval"` // seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigManager interface defines methods for configuration management
|
||||||
|
type ConfigManager interface {
|
||||||
|
Load() (*Config, error)
|
||||||
|
Save(config *Config) error
|
||||||
|
GetConfigPath() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfigManager implements ConfigManager interface
|
||||||
|
type DefaultConfigManager struct {
|
||||||
|
configPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfigManager creates a new configuration manager
|
||||||
|
func NewConfigManager() ConfigManager {
|
||||||
|
configDir := getConfigDir()
|
||||||
|
configPath := filepath.Join(configDir, "config.yaml")
|
||||||
|
return &DefaultConfigManager{
|
||||||
|
configPath: configPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfigPath returns the path to the configuration file
|
||||||
|
func (cm *DefaultConfigManager) GetConfigPath() string {
|
||||||
|
return cm.configPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load reads and parses the configuration file
|
||||||
|
func (cm *DefaultConfigManager) Load() (*Config, error) {
|
||||||
|
// Create config directory if it doesn't exist
|
||||||
|
configDir := filepath.Dir(cm.configPath)
|
||||||
|
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create config directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If config file doesn't exist, create default config
|
||||||
|
if _, err := os.Stat(cm.configPath); os.IsNotExist(err) {
|
||||||
|
defaultConfig := getDefaultConfig()
|
||||||
|
if err := cm.Save(defaultConfig); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create default config: %w", err)
|
||||||
|
}
|
||||||
|
return defaultConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read existing config file
|
||||||
|
data, err := os.ReadFile(cm.configPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
if err := yaml.Unmarshal(data, &config); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and apply defaults for missing fields
|
||||||
|
if err := validateAndApplyDefaults(&config); err != nil {
|
||||||
|
return nil, fmt.Errorf("config validation failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save writes the configuration to file
|
||||||
|
func (cm *DefaultConfigManager) Save(config *Config) error {
|
||||||
|
// Validate config before saving
|
||||||
|
if err := validateConfig(config); err != nil {
|
||||||
|
return fmt.Errorf("config validation failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create config directory if it doesn't exist
|
||||||
|
configDir := filepath.Dir(cm.configPath)
|
||||||
|
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create config directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal config to YAML
|
||||||
|
data, err := yaml.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to file
|
||||||
|
if err := os.WriteFile(cm.configPath, data, 0644); err != nil {
|
||||||
|
return fmt.Errorf("failed to write config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDefaultConfig returns a configuration with default values
|
||||||
|
func getDefaultConfig() *Config {
|
||||||
|
// Try to load from embedded template first
|
||||||
|
if templateData, err := assets.GetConfigTemplate(); err == nil {
|
||||||
|
var config Config
|
||||||
|
if err := yaml.Unmarshal(templateData, &config); err == nil {
|
||||||
|
return &config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to hardcoded defaults if template loading fails
|
||||||
|
return &Config{
|
||||||
|
ResourceID: "M9A", // Default resource ID
|
||||||
|
CurrentVersion: "v1.0.0",
|
||||||
|
CDK: "",
|
||||||
|
UserAgent: "LightweightUpdater/1.0",
|
||||||
|
BackupURL: "",
|
||||||
|
LogLevel: "info",
|
||||||
|
AutoCheck: true,
|
||||||
|
CheckInterval: 3600, // 1 hour
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateConfig validates the configuration values
|
||||||
|
func validateConfig(config *Config) error {
|
||||||
|
if config == nil {
|
||||||
|
return fmt.Errorf("config cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ResourceID == "" {
|
||||||
|
return fmt.Errorf("resource_id cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.CurrentVersion == "" {
|
||||||
|
return fmt.Errorf("current_version cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.UserAgent == "" {
|
||||||
|
return fmt.Errorf("user_agent cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
validLogLevels := map[string]bool{
|
||||||
|
"debug": true,
|
||||||
|
"info": true,
|
||||||
|
"warn": true,
|
||||||
|
"error": true,
|
||||||
|
}
|
||||||
|
if !validLogLevels[config.LogLevel] {
|
||||||
|
return fmt.Errorf("invalid log_level: %s (must be debug, info, warn, or error)", config.LogLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.CheckInterval < 60 {
|
||||||
|
return fmt.Errorf("check_interval must be at least 60 seconds")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateAndApplyDefaults validates config and applies defaults for missing fields
|
||||||
|
func validateAndApplyDefaults(config *Config) error {
|
||||||
|
defaults := getDefaultConfig()
|
||||||
|
|
||||||
|
// Apply defaults for empty fields
|
||||||
|
if config.UserAgent == "" {
|
||||||
|
config.UserAgent = defaults.UserAgent
|
||||||
|
}
|
||||||
|
if config.LogLevel == "" {
|
||||||
|
config.LogLevel = defaults.LogLevel
|
||||||
|
}
|
||||||
|
if config.CheckInterval == 0 {
|
||||||
|
config.CheckInterval = defaults.CheckInterval
|
||||||
|
}
|
||||||
|
if config.CurrentVersion == "" {
|
||||||
|
config.CurrentVersion = defaults.CurrentVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate after applying defaults
|
||||||
|
return validateConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getConfigDir returns the configuration directory path
|
||||||
|
func getConfigDir() string {
|
||||||
|
// Use APPDATA on Windows, fallback to current directory
|
||||||
|
if appData := os.Getenv("APPDATA"); appData != "" {
|
||||||
|
return filepath.Join(appData, "LightweightUpdater")
|
||||||
|
}
|
||||||
|
return "."
|
||||||
|
}
|
||||||
|
|
||||||
|
// encryptCDK encrypts the CDK using XOR encryption with a static key
|
||||||
|
func encryptCDK(cdk string) string {
|
||||||
|
if cdk == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
key := []byte("updater-key-2024")
|
||||||
|
encrypted := make([]byte, len(cdk))
|
||||||
|
|
||||||
|
for i, b := range []byte(cdk) {
|
||||||
|
encrypted[i] = b ^ key[i%len(key)]
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(encrypted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decryptCDK decrypts the CDK using XOR decryption with a static key
|
||||||
|
func decryptCDK(encryptedCDK string) (string, error) {
|
||||||
|
if encryptedCDK == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted, err := base64.StdEncoding.DecodeString(encryptedCDK)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to decode encrypted CDK: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := []byte("updater-key-2024")
|
||||||
|
decrypted := make([]byte, len(encrypted))
|
||||||
|
|
||||||
|
for i, b := range encrypted {
|
||||||
|
decrypted[i] = b ^ key[i%len(key)]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(decrypted), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCDK sets the CDK in the config with encryption
|
||||||
|
func (c *Config) SetCDK(cdk string) {
|
||||||
|
c.CDK = encryptCDK(cdk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCDK returns the decrypted CDK from the config
|
||||||
|
func (c *Config) GetCDK() (string, error) {
|
||||||
|
return decryptCDK(c.CDK)
|
||||||
|
}
|
||||||
56
Go_Updater/config/config.json
Normal file
56
Go_Updater/config/config.json
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"Function": {
|
||||||
|
"BossKey": "",
|
||||||
|
"HistoryRetentionTime": 0,
|
||||||
|
"HomeImageMode": "默认",
|
||||||
|
"IfAgreeBilibili": true,
|
||||||
|
"IfAllowSleep": false,
|
||||||
|
"IfSilence": false,
|
||||||
|
"IfSkipMumuSplashAds": false,
|
||||||
|
"UnattendedMode": false
|
||||||
|
},
|
||||||
|
"Notify": {
|
||||||
|
"AuthorizationCode": "",
|
||||||
|
"CompanyWebHookBotUrl": "",
|
||||||
|
"FromAddress": "",
|
||||||
|
"IfCompanyWebHookBot": false,
|
||||||
|
"IfPushPlyer": false,
|
||||||
|
"IfSendMail": false,
|
||||||
|
"IfSendSixStar": false,
|
||||||
|
"IfSendStatistic": false,
|
||||||
|
"IfServerChan": false,
|
||||||
|
"SMTPServerAddress": "",
|
||||||
|
"SendTaskResultTime": "不推送",
|
||||||
|
"ServerChanChannel": "",
|
||||||
|
"ServerChanKey": "",
|
||||||
|
"ServerChanTag": "",
|
||||||
|
"ToAddress": ""
|
||||||
|
},
|
||||||
|
"Start": {
|
||||||
|
"IfMinimizeDirectly": false,
|
||||||
|
"IfRunDirectly": false,
|
||||||
|
"IfSelfStart": false
|
||||||
|
},
|
||||||
|
"QFluentWidgets": {
|
||||||
|
"ThemeColor": "#ff009faa",
|
||||||
|
"ThemeMode": "Dark"
|
||||||
|
},
|
||||||
|
"UI": {
|
||||||
|
"IfShowTray": false,
|
||||||
|
"IfToTray": false,
|
||||||
|
"location": "100x100",
|
||||||
|
"maximized": false,
|
||||||
|
"size": "1200x700"
|
||||||
|
},
|
||||||
|
"Update": {
|
||||||
|
"IfAutoUpdate": false,
|
||||||
|
"MirrorChyanCDK": "",
|
||||||
|
"ProxyUrlList": [],
|
||||||
|
"ThreadNumb": 8,
|
||||||
|
"UpdateType": "stable"
|
||||||
|
},
|
||||||
|
"Voice": {
|
||||||
|
"Enabled": false,
|
||||||
|
"Type": "simple"
|
||||||
|
}
|
||||||
|
}
|
||||||
472
Go_Updater/config/config_test.go
Normal file
472
Go_Updater/config/config_test.go
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncryptDecryptCDK(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
original string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Empty CDK",
|
||||||
|
original: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Simple CDK",
|
||||||
|
original: "test123",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Complex CDK",
|
||||||
|
original: "ABC123-DEF456-GHI789",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CDK with special characters",
|
||||||
|
original: "test@#$%^&*()_+-={}[]|\\:;\"'<>?,./",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Long CDK",
|
||||||
|
original: "this-is-a-very-long-cdk-key-that-should-still-work-properly-with-encryption-and-decryption",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Test encryption
|
||||||
|
encrypted := encryptCDK(tt.original)
|
||||||
|
|
||||||
|
// Empty string should remain empty
|
||||||
|
if tt.original == "" {
|
||||||
|
if encrypted != "" {
|
||||||
|
t.Errorf("Expected empty string for empty input, got %s", encrypted)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypted should be different from original (unless original is empty)
|
||||||
|
if encrypted == tt.original {
|
||||||
|
t.Errorf("Encrypted CDK should be different from original")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test decryption
|
||||||
|
decrypted, err := decryptCDK(encrypted)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Decryption failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypted should match original
|
||||||
|
if decrypted != tt.original {
|
||||||
|
t.Errorf("Expected %s, got %s", tt.original, decrypted)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigSetGetCDK(t *testing.T) {
|
||||||
|
config := &Config{}
|
||||||
|
|
||||||
|
testCDK := "test-cdk-123"
|
||||||
|
|
||||||
|
// Set CDK (should encrypt)
|
||||||
|
config.SetCDK(testCDK)
|
||||||
|
|
||||||
|
// CDK field should be encrypted (different from original)
|
||||||
|
if config.CDK == testCDK {
|
||||||
|
t.Errorf("CDK should be encrypted in config")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get CDK (should decrypt)
|
||||||
|
retrievedCDK, err := config.GetCDK()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to get CDK: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if retrievedCDK != testCDK {
|
||||||
|
t.Errorf("Expected %s, got %s", testCDK, retrievedCDK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecryptInvalidCDK(t *testing.T) {
|
||||||
|
// Test with invalid base64
|
||||||
|
_, err := decryptCDK("invalid-base64!")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error for invalid base64")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigManagerLoadSave(t *testing.T) {
|
||||||
|
// Create temporary directory for test
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
// Create config manager with temp path
|
||||||
|
cm := &DefaultConfigManager{
|
||||||
|
configPath: filepath.Join(tempDir, "test-config.yaml"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test loading non-existent config (should create default)
|
||||||
|
config, err := cm.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to load config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
t.Errorf("Config should not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify default values
|
||||||
|
if config.CurrentVersion != "v1.0.0" {
|
||||||
|
t.Errorf("Expected default version v1.0.0, got %s", config.CurrentVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.UserAgent != "LightweightUpdater/1.0" {
|
||||||
|
t.Errorf("Expected default user agent, got %s", config.UserAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set some values including CDK
|
||||||
|
config.ResourceID = "TEST123"
|
||||||
|
config.SetCDK("secret-cdk-key")
|
||||||
|
|
||||||
|
// Save config
|
||||||
|
err = cm.Save(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to save config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config again
|
||||||
|
loadedConfig, err := cm.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to load saved config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify values
|
||||||
|
if loadedConfig.ResourceID != "TEST123" {
|
||||||
|
t.Errorf("Expected ResourceID TEST123, got %s", loadedConfig.ResourceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify CDK is properly encrypted/decrypted
|
||||||
|
retrievedCDK, err := loadedConfig.GetCDK()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to get CDK from loaded config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if retrievedCDK != "secret-cdk-key" {
|
||||||
|
t.Errorf("Expected CDK secret-cdk-key, got %s", retrievedCDK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify CDK is encrypted in the config struct
|
||||||
|
if loadedConfig.CDK == "secret-cdk-key" {
|
||||||
|
t.Errorf("CDK should be encrypted in config file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigValidation(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
config *Config
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Nil config",
|
||||||
|
config: nil,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty ResourceID",
|
||||||
|
config: &Config{
|
||||||
|
ResourceID: "",
|
||||||
|
CurrentVersion: "v1.0.0",
|
||||||
|
UserAgent: "Test/1.0",
|
||||||
|
LogLevel: "info",
|
||||||
|
CheckInterval: 3600,
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty CurrentVersion",
|
||||||
|
config: &Config{
|
||||||
|
ResourceID: "TEST",
|
||||||
|
CurrentVersion: "",
|
||||||
|
UserAgent: "Test/1.0",
|
||||||
|
LogLevel: "info",
|
||||||
|
CheckInterval: 3600,
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid LogLevel",
|
||||||
|
config: &Config{
|
||||||
|
ResourceID: "TEST",
|
||||||
|
CurrentVersion: "v1.0.0",
|
||||||
|
UserAgent: "Test/1.0",
|
||||||
|
LogLevel: "invalid",
|
||||||
|
CheckInterval: 3600,
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid CheckInterval",
|
||||||
|
config: &Config{
|
||||||
|
ResourceID: "TEST",
|
||||||
|
CurrentVersion: "v1.0.0",
|
||||||
|
UserAgent: "Test/1.0",
|
||||||
|
LogLevel: "info",
|
||||||
|
CheckInterval: 30, // Less than 60
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid config",
|
||||||
|
config: &Config{
|
||||||
|
ResourceID: "TEST",
|
||||||
|
CurrentVersion: "v1.0.0",
|
||||||
|
UserAgent: "Test/1.0",
|
||||||
|
LogLevel: "info",
|
||||||
|
CheckInterval: 3600,
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := validateConfig(tt.config)
|
||||||
|
if tt.expectError && err == nil {
|
||||||
|
t.Errorf("Expected error but got none")
|
||||||
|
}
|
||||||
|
if !tt.expectError && err != nil {
|
||||||
|
t.Errorf("Expected no error but got: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetConfigDir(t *testing.T) {
|
||||||
|
// Save original APPDATA
|
||||||
|
originalAppData := os.Getenv("APPDATA")
|
||||||
|
defer os.Setenv("APPDATA", originalAppData)
|
||||||
|
|
||||||
|
// Test with APPDATA set
|
||||||
|
os.Setenv("APPDATA", "C:\\Users\\Test\\AppData\\Roaming")
|
||||||
|
dir := getConfigDir()
|
||||||
|
expected := "C:\\Users\\Test\\AppData\\Roaming\\LightweightUpdater"
|
||||||
|
if dir != expected {
|
||||||
|
t.Errorf("Expected %s, got %s", expected, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test without APPDATA
|
||||||
|
os.Unsetenv("APPDATA")
|
||||||
|
dir = getConfigDir()
|
||||||
|
if dir != "." {
|
||||||
|
t.Errorf("Expected current directory, got %s", dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateAndApplyDefaults(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input *Config
|
||||||
|
expected *Config
|
||||||
|
hasError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Apply defaults to empty config",
|
||||||
|
input: &Config{
|
||||||
|
ResourceID: "TEST",
|
||||||
|
},
|
||||||
|
expected: &Config{
|
||||||
|
ResourceID: "TEST",
|
||||||
|
CurrentVersion: "v1.0.0",
|
||||||
|
UserAgent: "LightweightUpdater/1.0",
|
||||||
|
LogLevel: "info",
|
||||||
|
CheckInterval: 3600,
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Partial config with some defaults needed",
|
||||||
|
input: &Config{
|
||||||
|
ResourceID: "TEST",
|
||||||
|
CurrentVersion: "v2.0.0",
|
||||||
|
LogLevel: "debug",
|
||||||
|
},
|
||||||
|
expected: &Config{
|
||||||
|
ResourceID: "TEST",
|
||||||
|
CurrentVersion: "v2.0.0",
|
||||||
|
UserAgent: "LightweightUpdater/1.0",
|
||||||
|
LogLevel: "debug",
|
||||||
|
CheckInterval: 3600,
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Config with invalid values after defaults",
|
||||||
|
input: &Config{
|
||||||
|
ResourceID: "", // Invalid - empty
|
||||||
|
CheckInterval: 30, // Invalid - too small
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := validateAndApplyDefaults(tt.input)
|
||||||
|
|
||||||
|
if tt.hasError {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error but got none")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that defaults were applied correctly
|
||||||
|
if tt.input.CurrentVersion != tt.expected.CurrentVersion {
|
||||||
|
t.Errorf("CurrentVersion: expected %s, got %s", tt.expected.CurrentVersion, tt.input.CurrentVersion)
|
||||||
|
}
|
||||||
|
if tt.input.UserAgent != tt.expected.UserAgent {
|
||||||
|
t.Errorf("UserAgent: expected %s, got %s", tt.expected.UserAgent, tt.input.UserAgent)
|
||||||
|
}
|
||||||
|
if tt.input.LogLevel != tt.expected.LogLevel {
|
||||||
|
t.Errorf("LogLevel: expected %s, got %s", tt.expected.LogLevel, tt.input.LogLevel)
|
||||||
|
}
|
||||||
|
if tt.input.CheckInterval != tt.expected.CheckInterval {
|
||||||
|
t.Errorf("CheckInterval: expected %d, got %d", tt.expected.CheckInterval, tt.input.CheckInterval)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDefaultConfig(t *testing.T) {
|
||||||
|
config := getDefaultConfig()
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
t.Fatal("getDefaultConfig() returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify default values
|
||||||
|
if config.ResourceID != "PLACEHOLDER" {
|
||||||
|
t.Errorf("Expected ResourceID 'PLACEHOLDER', got %s", config.ResourceID)
|
||||||
|
}
|
||||||
|
if config.CurrentVersion != "v1.0.0" {
|
||||||
|
t.Errorf("Expected CurrentVersion 'v1.0.0', got %s", config.CurrentVersion)
|
||||||
|
}
|
||||||
|
if config.UserAgent != "LightweightUpdater/1.0" {
|
||||||
|
t.Errorf("Expected UserAgent 'LightweightUpdater/1.0', got %s", config.UserAgent)
|
||||||
|
}
|
||||||
|
if config.LogLevel != "info" {
|
||||||
|
t.Errorf("Expected LogLevel 'info', got %s", config.LogLevel)
|
||||||
|
}
|
||||||
|
if config.CheckInterval != 3600 {
|
||||||
|
t.Errorf("Expected CheckInterval 3600, got %d", config.CheckInterval)
|
||||||
|
}
|
||||||
|
if !config.AutoCheck {
|
||||||
|
t.Errorf("Expected AutoCheck true, got %v", config.AutoCheck)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigManagerWithCustomPath(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
customPath := filepath.Join(tempDir, "custom-config.yaml")
|
||||||
|
|
||||||
|
cm := &DefaultConfigManager{
|
||||||
|
configPath: customPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test GetConfigPath
|
||||||
|
if cm.GetConfigPath() != customPath {
|
||||||
|
t.Errorf("Expected config path %s, got %s", customPath, cm.GetConfigPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Save and Load with custom path
|
||||||
|
testConfig := &Config{
|
||||||
|
ResourceID: "CUSTOM",
|
||||||
|
CurrentVersion: "v1.5.0",
|
||||||
|
UserAgent: "CustomUpdater/1.0",
|
||||||
|
LogLevel: "debug",
|
||||||
|
CheckInterval: 7200,
|
||||||
|
AutoCheck: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save config
|
||||||
|
err := cm.Save(testConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to save config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
loadedConfig, err := cm.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify loaded config matches saved config
|
||||||
|
if loadedConfig.ResourceID != testConfig.ResourceID {
|
||||||
|
t.Errorf("ResourceID mismatch: expected %s, got %s", testConfig.ResourceID, loadedConfig.ResourceID)
|
||||||
|
}
|
||||||
|
if loadedConfig.CurrentVersion != testConfig.CurrentVersion {
|
||||||
|
t.Errorf("CurrentVersion mismatch: expected %s, got %s", testConfig.CurrentVersion, loadedConfig.CurrentVersion)
|
||||||
|
}
|
||||||
|
if loadedConfig.AutoCheck != testConfig.AutoCheck {
|
||||||
|
t.Errorf("AutoCheck mismatch: expected %v, got %v", testConfig.AutoCheck, loadedConfig.AutoCheck)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigManagerErrorHandling(t *testing.T) {
|
||||||
|
// Test with invalid directory path
|
||||||
|
invalidPath := string([]byte{0}) + "/invalid/config.yaml"
|
||||||
|
cm := &DefaultConfigManager{
|
||||||
|
configPath: invalidPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load should fail with invalid path
|
||||||
|
_, err := cm.Load()
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error when loading from invalid path")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save should fail with invalid path
|
||||||
|
testConfig := getDefaultConfig()
|
||||||
|
testConfig.ResourceID = "TEST"
|
||||||
|
err = cm.Save(testConfig)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error when saving to invalid path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncryptDecryptEdgeCases(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
}{
|
||||||
|
{"Unicode characters", "测试CDK密钥🔑"},
|
||||||
|
{"Very long string", strings.Repeat("A", 1000)},
|
||||||
|
{"Binary-like data", string([]byte{0, 1, 2, 3, 255, 254, 253})},
|
||||||
|
{"Only spaces", " "},
|
||||||
|
{"Newlines and tabs", "line1\nline2\tindented"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
encrypted := encryptCDK(tt.input)
|
||||||
|
decrypted, err := decryptCDK(encrypted)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Decryption failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if decrypted != tt.input {
|
||||||
|
t.Errorf("Encryption/decryption mismatch: expected %q, got %q", tt.input, decrypted)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user