feat: 飞牛NAS成本审核更新 - 新增SKU、更新价格表、清理归档及订单记录,同步auto-check改动

This commit is contained in:
12600k-rog-d4
2026-08-27 00:44:44 +08:00
parent bde60dbea0
commit a172a9bb5e
82 changed files with 1804 additions and 1180 deletions

View File

@@ -0,0 +1,131 @@
package report
import (
"bytes"
"strings"
"testing"
)
// TestEscposQR_EmptyData 空数据应直接返回 nil不写入任何指令
func TestEscposQR_EmptyData(t *testing.T) {
var buf bytes.Buffer
if err := escposQR(&buf, "", 6, 49); err != nil {
t.Fatalf("空数据应返回 nil得到 err=%v", err)
}
if buf.Len() != 0 {
t.Errorf("空数据不应输出指令,实际写入 %d 字节", buf.Len())
}
}
// TestEscposQR_BasicCommands 验证基本 GS ( k 指令流
func TestEscposQR_BasicCommands(t *testing.T) {
var buf bytes.Buffer
data := "http://192.168.1.10:8080/reports/report-abc-10.0.0.1-20260824.html"
if err := escposQR(&buf, data, 6, 49); err != nil {
t.Fatalf("生成指令失败: %v", err)
}
got := buf.Bytes()
// 必须包含 GS ( k 起始
if !bytes.Contains(got, []byte{0x1d, 0x28, 0x6b}) {
t.Error("缺少 GS ( k 起始指令")
}
// 必须包含模型选择 cn=49 fn=65 n=50
if !bytes.Contains(got, []byte{0x31, 0x41, 0x32}) {
t.Error("缺少 QR 模型选择指令 (31 41 32)")
}
// 必须包含模块大小 n=6
if !bytes.Contains(got, []byte{0x31, 0x43, 0x06}) {
t.Error("缺少模块大小指令 (31 43 06)")
}
// 必须包含纠错级别 M (49)
if !bytes.Contains(got, []byte{0x31, 0x45, 49}) {
t.Error("缺少纠错级别指令 (31 45 49)")
}
// 必须包含数据存储 49 80 48
if !bytes.Contains(got, []byte{0x31, 0x50, 0x30}) {
t.Error("缺少数据存储指令 (31 50 30)")
}
// 必须包含打印指令 49 81 48
if !bytes.Contains(got, []byte{0x31, 0x51, 0x30}) {
t.Error("缺少打印指令 (31 51 30)")
}
// 数据本体
if !bytes.Contains(got, []byte(data)) {
t.Error("缺少 URL 数据本体")
}
}
// TestEscposQR_ClampSizeAndECC 验证 size/ecc 参数越界时自动夹紧
func TestEscposQR_ClampSizeAndECC(t *testing.T) {
var buf1, buf2, buf3 bytes.Buffer
// size 越界 0 → 夹紧为 1
_ = escposQR(&buf1, "abc", 0, 49)
// size 越界 99 → 夹紧为 16
_ = escposQR(&buf2, "abc", 99, 49)
// ecc 非法 99 → 夹紧为 49 (M)
_ = escposQR(&buf3, "abc", 6, 99)
if !bytes.Contains(buf1.Bytes(), []byte{0x31, 0x43, 0x01}) {
t.Error("size=0 应夹紧为 1未生效")
}
if !bytes.Contains(buf2.Bytes(), []byte{0x31, 0x43, 0x10}) {
t.Error("size=99 应夹紧为 16未生效")
}
if !bytes.Contains(buf3.Bytes(), []byte{0x31, 0x45, 49}) {
t.Error("ecc=99 应夹紧为 49(M),未生效")
}
}
// TestEscposQR_DataLengthEncoding pL/pH 编码正确(大数据场景)
// 指令布局: 1d 28 6b pL pH 31 50 30 <data>
func TestEscposQR_DataLengthEncoding(t *testing.T) {
var buf bytes.Buffer
// 200 字节数据 → pL = 203 (0xCB), pH = 0
data := strings.Repeat("a", 200)
_ = escposQR(&buf, data, 6, 49)
got := buf.Bytes()
wantPL := byte((200 + 3) & 0xff) // 203 = 0xCB
wantPH := byte(((200 + 3) >> 8) & 0xff)
// 数据存储标记 "31 50 30" 在 got 中的位置
dataMarker := []byte{0x31, 0x50, 0x30}
idx := bytes.Index(got, dataMarker)
if idx < 0 {
t.Fatal("未找到数据存储指令 31 50 30")
}
// 期望布局1d(GS) 28 ( 6b k pL pH 31 50 30 ...
// got[idx-5] = 0x1d, got[idx-4] = 0x28, got[idx-3] = 0x6b
// got[idx-2] = pL, got[idx-1] = pH
if got[idx-5] != 0x1d || got[idx-4] != 0x28 || got[idx-3] != 0x6b {
t.Fatalf("pL/pH 前不是 GS ( k实际前 5 字节 = % x", got[idx-5:idx])
}
if got[idx-2] != wantPL || got[idx-1] != wantPH {
t.Errorf("pL/pH 编码错误: got=0x%02x 0x%02x, want=0x%02x 0x%02x",
got[idx-2], got[idx-1], wantPL, wantPH)
}
}
// TestSystemPrinter_PrintQR 系统打印后端 no-op
func TestSystemPrinter_PrintQR(t *testing.T) {
p := &SystemPrinter{}
if err := p.PrintQR("http://example.com"); err != nil {
t.Errorf("SystemPrinter.PrintQR 应返回 nil得到 %v", err)
}
}
// TestFilePrinter_PrintQR 文件后端写入提示行 + URL
func TestFilePrinter_PrintQR(t *testing.T) {
tmp := t.TempDir()
p, err := NewFilePrinter(tmp + "/out.log")
if err != nil {
t.Fatalf("创建 FilePrinter 失败: %v", err)
}
defer p.Close()
url := "http://192.168.1.10/reports/r.html"
if err := p.PrintQR(url); err != nil {
t.Fatalf("PrintQR 失败: %v", err)
}
}