mirror of
https://github.com/alireza0/s-ui.git
synced 2026-10-21 03:12:06 +00:00
core: add config compatibility test for the 1.14 upgrade
Starts a real box for each config in testdata/configs and logs the deprecation notes it triggers. Covers the eight re-copied protocol inbounds plus the option shapes s-ui's UI can emit that 1.14 deprecates. Confirms no config s-ui produces fails to start under 1.14; the deprecated ones warn and keep working.
This commit is contained in:
parent
afe16b4d2d
commit
6f046508db
7 changed files with 181 additions and 0 deletions
125
core/config_compat_test.go
Normal file
125
core/config_compat_test.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alireza0/s-ui/logger"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
// collectManager records deprecation notes instead of writing them to stderr,
|
||||
// so tests can assert on which ones a config triggers.
|
||||
type collectManager struct{ notes []deprecated.Note }
|
||||
|
||||
func (c *collectManager) ReportDeprecated(feature deprecated.Note) {
|
||||
c.notes = append(c.notes, feature)
|
||||
}
|
||||
|
||||
// writeTestCert generates a throwaway self-signed cert for the TLS-bearing
|
||||
// inbounds in testdata, returning the certificate and key paths.
|
||||
func writeTestCert(t *testing.T) (string, string) {
|
||||
t.Helper()
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tmpl := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{CommonName: "t.example.com"},
|
||||
DNSNames: []string{"t.example.com"},
|
||||
NotBefore: time.Now().Add(-time.Hour),
|
||||
NotAfter: time.Now().Add(24 * time.Hour),
|
||||
}
|
||||
der, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &key.PublicKey, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
keyDER, err := x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dir := t.TempDir()
|
||||
certPath := filepath.Join(dir, "cert.pem")
|
||||
keyPath := filepath.Join(dir, "key.pem")
|
||||
writePEM(t, certPath, "CERTIFICATE", der)
|
||||
writePEM(t, keyPath, "EC PRIVATE KEY", keyDER)
|
||||
return certPath, keyPath
|
||||
}
|
||||
|
||||
func writePEM(t *testing.T, path, blockType string, der []byte) {
|
||||
t.Helper()
|
||||
var buf bytes.Buffer
|
||||
if err := pem.Encode(&buf, &pem.Block{Type: blockType, Bytes: der}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(path, buf.Bytes(), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestConfigCompat starts a box for each config in testdata/configs and reports
|
||||
// the deprecation notes it triggers. It guards the sing-box upgrade: configs
|
||||
// s-ui's UI can produce must keep starting, and any new deprecation shows up in
|
||||
// the test log so it can be migrated before release.
|
||||
func TestConfigCompat(t *testing.T) {
|
||||
logger.InitLogger(logging.ERROR)
|
||||
certPath, keyPath := writeTestCert(t)
|
||||
|
||||
files, err := filepath.Glob(filepath.Join("testdata", "configs", "*.json"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
t.Fatal("no test configs found")
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
t.Run(filepath.Base(file), func(t *testing.T) {
|
||||
raw, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data := strings.NewReplacer("__CERT__", certPath, "__KEY__", keyPath).Replace(string(raw))
|
||||
|
||||
ctx := Context(context.Background(), InboundRegistry(), OutboundRegistry(),
|
||||
EndpointRegistry(), DNSTransportRegistry(), ServiceRegistry(), CertificateProviderRegistry())
|
||||
notes := &collectManager{}
|
||||
ctx = service.ContextWith[deprecated.Manager](ctx, notes)
|
||||
|
||||
var opts option.Options
|
||||
if err = opts.UnmarshalJSONContext(ctx, []byte(data)); err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
|
||||
instance, err := NewBox(Options{Context: ctx, Options: opts})
|
||||
if err != nil {
|
||||
t.Fatalf("create box: %v", err)
|
||||
}
|
||||
defer instance.Close()
|
||||
if err = instance.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
|
||||
for _, note := range notes.notes {
|
||||
t.Logf("deprecated: %s -- %s", note.Name, note.Description)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
8
core/testdata/configs/02-dns-independent-cache.json
vendored
Normal file
8
core/testdata/configs/02-dns-independent-cache.json
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"dns": {
|
||||
"servers": [{"type": "udp", "tag": "google", "server": "8.8.8.8"}],
|
||||
"rules": [],
|
||||
"independent_cache": true
|
||||
},
|
||||
"outbounds": [{"type": "direct", "tag": "direct"}]
|
||||
}
|
||||
7
core/testdata/configs/03-dns-rule-strategy.json
vendored
Normal file
7
core/testdata/configs/03-dns-rule-strategy.json
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"dns": {
|
||||
"servers": [{"type": "udp", "tag": "google", "server": "8.8.8.8"}],
|
||||
"rules": [{"domain": ["example.com"], "action": "route", "server": "google", "strategy": "ipv4_only"}]
|
||||
},
|
||||
"outbounds": [{"type": "direct", "tag": "direct"}]
|
||||
}
|
||||
4
core/testdata/configs/06-store-rdrc.json
vendored
Normal file
4
core/testdata/configs/06-store-rdrc.json
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"experimental": {"cache_file": {"enabled": true, "store_rdrc": true}},
|
||||
"outbounds": [{"type": "direct", "tag": "direct"}]
|
||||
}
|
||||
6
core/testdata/configs/07-sui-default.json
vendored
Normal file
6
core/testdata/configs/07-sui-default.json
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"log": {"level": "info"},
|
||||
"dns": {"servers": [], "rules": []},
|
||||
"route": {"rules": [{"action": "sniff"}, {"protocol": ["dns"], "action": "hijack-dns"}]},
|
||||
"experimental": {}
|
||||
}
|
||||
26
core/testdata/configs/08-full-protocols.json
vendored
Normal file
26
core/testdata/configs/08-full-protocols.json
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"log": {"level": "error"},
|
||||
"inbounds": [
|
||||
{"type": "shadowsocks", "tag": "ss-in", "listen": "127.0.0.1", "listen_port": 42001,
|
||||
"method": "2022-blake3-aes-128-gcm", "password": "8JCsPssfgS8tiRwiMlhARg=="},
|
||||
{"type": "vmess", "tag": "vmess-in", "listen": "127.0.0.1", "listen_port": 42002,
|
||||
"users": [{"uuid": "b831381d-6324-4d53-ad4f-8cda48b30811", "alterId": 0}]},
|
||||
{"type": "vless", "tag": "vless-in", "listen": "127.0.0.1", "listen_port": 42003,
|
||||
"users": [{"uuid": "b831381d-6324-4d53-ad4f-8cda48b30811"}]},
|
||||
{"type": "trojan", "tag": "trojan-in", "listen": "127.0.0.1", "listen_port": 42004,
|
||||
"users": [{"password": "trojanpass"}]},
|
||||
{"type": "anytls", "tag": "anytls-in", "listen": "127.0.0.1", "listen_port": 42005,
|
||||
"users": [{"password": "anytlspass"}],
|
||||
"tls": {"enabled": true, "server_name": "t.example.com", "key_path": "__KEY__", "certificate_path": "__CERT__"}},
|
||||
{"type": "hysteria2", "tag": "hy2-in", "listen": "127.0.0.1", "listen_port": 42006,
|
||||
"users": [{"password": "hy2pass"}],
|
||||
"tls": {"enabled": true, "server_name": "t.example.com", "alpn": ["h3"], "key_path": "__KEY__", "certificate_path": "__CERT__"}},
|
||||
{"type": "tuic", "tag": "tuic-in", "listen": "127.0.0.1", "listen_port": 42007,
|
||||
"users": [{"uuid": "b831381d-6324-4d53-ad4f-8cda48b30811", "password": "tuicpass"}],
|
||||
"tls": {"enabled": true, "server_name": "t.example.com", "alpn": ["h3"], "key_path": "__KEY__", "certificate_path": "__CERT__"}},
|
||||
{"type": "hysteria", "tag": "hy1-in", "listen": "127.0.0.1", "listen_port": 42008,
|
||||
"up_mbps": 100, "down_mbps": 100, "users": [{"auth_str": "hy1pass"}],
|
||||
"tls": {"enabled": true, "server_name": "t.example.com", "alpn": ["h3"], "key_path": "__KEY__", "certificate_path": "__CERT__"}}
|
||||
],
|
||||
"outbounds": [{"type": "direct", "tag": "direct"}]
|
||||
}
|
||||
5
core/testdata/configs/10-dns-ipcidr.json
vendored
Normal file
5
core/testdata/configs/10-dns-ipcidr.json
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"dns": {"servers": [{"type": "udp", "tag": "google", "server": "8.8.8.8"}],
|
||||
"rules": [{"ip_cidr": ["10.0.0.0/8"], "action": "route", "server": "google"}]},
|
||||
"outbounds": [{"type": "direct", "tag": "direct"}]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue