Переглянути джерело

更新代码并添加单元测试

移除了未使用的导入,简化了`Location`变量的定义,并将`initName`函数从`variabl.go`移动到了`resource.go`中。同时,为`resource`和`utils`包新增了多个单元测试以确保其功能正确性。
SongZihuan 1 тиждень тому
батько
коміт
2ff652fb6f
8 змінених файлів з 374 додано та 23 видалено
  1. 10 0
      CHANGELOG.md
  2. 33 9
      resource.go
  3. 82 0
      resource_test.go
  4. 63 0
      service_test.go
  5. 1 12
      src/global/variabl.go
  6. 9 2
      src/utils/osutils/export.go
  7. 10 0
      utils.go
  8. 166 0
      utils_test.go

+ 10 - 0
CHANGELOG.md

@@ -5,6 +5,16 @@
 其格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
 且本项目遵循 [语义化版本控制](https://semver.org/lang/zh-CN/)。
 
+## [未发布]
+
+### 新增
+
+- 新增单元测试(以后关于测试的代码变化将记录于 “测试” 小节中)。
+
+### 测试
+
+- 新增`resource`包的测试。
+
 ## [0.3.0] - 2025-04-17
 
 ### 新增

+ 33 - 9
resource.go

@@ -3,6 +3,8 @@ package resource
 import (
 	_ "embed"
 	"fmt"
+	"os"
+	"path/filepath"
 	"strconv"
 	"strings"
 	"time"
@@ -40,7 +42,10 @@ var GitTagCommitHash string
 var randomData string
 
 func init() {
-	initCleanFile()
+	initCleanFile() // 最先执行
+
+	// 其他操作
+	initName()
 	initBuildDate()
 	initVersion()
 	initServiceConfig()
@@ -58,6 +63,33 @@ func initCleanFile() {
 	GitTagCommitHash = utilsClenFileData(GitTagCommitHash)
 }
 
+func initName() {
+	if Name != "" {
+		return
+	}
+
+	_args0, err := os.Executable()
+	if err != nil {
+		if len(os.Args) > 0 {
+			_args0 = os.Args[0]
+		} else {
+			panic(fmt.Sprintf("name was empty: %s", err.Error()))
+		}
+	}
+
+	if _args0 == "" {
+		panic("name was empty")
+	}
+
+	_args0Name := filepath.Base(_args0)
+
+	if _args0Name == "" {
+		panic("name was empty")
+	}
+
+	Name = _args0Name
+}
+
 func initBuildDate() {
 	if buildDateTxt == "" {
 		BuildTime = time.Now()
@@ -121,11 +153,3 @@ func getGitTagVersion() (gitVer string) {
 func getRandomVersion() (randVer string) {
 	return fmt.Sprintf("0.0.0+dev-%d-%s", BuildTime.Unix(), randomData)
 }
-
-func utilsClenFileData(data string) (res string) {
-	res = utilsCheckAndRemoveBOM(data)
-	res = strings.Replace(res, "\r", "", -1)
-	res = strings.Split(res, "\n")[0]
-	res = strings.TrimSpace(res)
-	return res
-}

+ 82 - 0
resource_test.go

@@ -0,0 +1,82 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package resource
+
+import "testing"
+
+func TestVersion(t *testing.T) {
+	t.Run("Version-Variable", func(t *testing.T) {
+		if Version == "" {
+			t.Fatalf("Version is empty")
+		} else if SemanticVersioning == "" {
+			t.Fatalf("SemanticVersioning is empty")
+		} else if "v"+SemanticVersioning != Version {
+			t.Fatalf("SemanticVersioning and Version do not match")
+		} else if !utilsIsSemanticVersion(SemanticVersioning) {
+			t.Fatalf("Non-semantic version")
+		}
+	})
+
+	t.Run("Default-Version", func(t *testing.T) {
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Get default version panic: %v", err)
+			}
+		}()
+		_ = getDefaultVersion()
+	})
+
+	t.Run("Git-Version", func(t *testing.T) {
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Get git version panic: %v", err)
+			}
+		}()
+		_ = getGitTagVersion()
+	})
+
+	t.Run("Random-Version", func(t *testing.T) {
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Get random version panic: %v", err)
+			}
+		}()
+
+		ver := getRandomVersion()
+		if ver == "" {
+			t.Fatalf("Random Version is empty")
+		}
+	})
+}
+
+func TestLicense(t *testing.T) {
+	if License == "" {
+		t.Fatalf("License is empty")
+	}
+}
+
+func TestReport(t *testing.T) {
+	if Report == "" {
+		t.Fatalf("Report is empty")
+	}
+}
+
+func TestName(t *testing.T) {
+	if Name == "" {
+		t.Fatalf("Name is empty")
+	}
+}
+
+func TestBuildDate(t *testing.T) {
+	if buildDateTxt == "" {
+		t.Fatalf("Build Date is empty")
+	}
+}
+
+func TestRandomData(t *testing.T) {
+	if randomData == "" {
+		t.Fatalf("Randome Data is empty")
+	}
+}

+ 63 - 0
service_test.go

@@ -0,0 +1,63 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package resource
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+)
+
+func TestServiceConfig(t *testing.T) {
+	if ServiceConfig.Name == "" {
+		t.Errorf("Service name is empty")
+	}
+
+	if ServiceConfig.DisplayName == "" {
+		t.Errorf("Service display name is empty")
+	}
+
+	if strings.Contains(ServiceConfig.Describe, "\n") {
+		t.Errorf("Service describe has LF")
+	}
+
+	if strings.Contains(ServiceConfig.Describe, "\r") {
+		t.Errorf("Service describe has CR")
+	}
+
+	if len(ServiceConfig.Describe) > 1 && ServiceConfig.Describe[0] == ' ' {
+		t.Errorf("Service describe start with space")
+	}
+
+	if len(ServiceConfig.Describe) > 2 && ServiceConfig.Describe[len(ServiceConfig.Describe)-1] == ' ' {
+		t.Errorf("Service describe end with space")
+	}
+
+	switch ServiceConfig.ArgumentFrom {
+	case FromConfig:
+		if len(ServiceConfig.ArgumentList) == 0 {
+			t.Errorf("Error argument-from: argument-list is empty, but argument-from config")
+		}
+	case FromNo:
+		if len(ServiceConfig.ArgumentList) > 0 {
+			t.Errorf("Error argument-from: argument-list is not empty, but argument-from no")
+		}
+	default:
+		t.Errorf(fmt.Sprintf("Error argument-from: %s", ServiceConfig.ArgumentFrom))
+	}
+
+	switch ServiceConfig.EnvFrom {
+	case FromConfig:
+		if len(ServiceConfig.EnvSetList) == 0 {
+			t.Errorf("Error env-from: env-set-list is empty, but env-from config")
+		}
+	case FromNo:
+		if len(ServiceConfig.EnvSetList) > 0 {
+			t.Errorf("Error env-from: env-set-list is not empty, but env-from no")
+		}
+	default:
+		t.Errorf(fmt.Sprintf("Error argument-from: %s", ServiceConfig.EnvFrom))
+	}
+}

+ 1 - 12
src/global/variabl.go

@@ -6,7 +6,6 @@ package global
 
 import (
 	resource "github.com/SongZihuan/BackendServerTemplate"
-	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
 	"time"
 )
 
@@ -28,15 +27,5 @@ var (
 
 // Location 以下变量需要在配置文件加载完毕后才可调用
 var (
-	Location *time.Location = time.UTC
+	Location = time.UTC
 )
-
-func init() {
-	initName()
-}
-
-func initName() {
-	if Name == "" {
-		Name = osutils.GetArgs0Name()
-	}
-}

+ 9 - 2
src/utils/osutils/export.go

@@ -10,6 +10,7 @@ import (
 )
 
 var _args0 = ""
+var _args0Name = ""
 
 func init() {
 	var err error
@@ -23,7 +24,13 @@ func init() {
 	}
 
 	if _args0 == "" {
-		panic("args was empty")
+		panic("args 0 was empty")
+	}
+
+	_args0Name = filepath.Base(_args0)
+
+	if _args0Name == "" {
+		panic("args 0 was empty")
 	}
 }
 
@@ -32,5 +39,5 @@ func GetArgs0() string {
 }
 
 func GetArgs0Name() string {
-	return filepath.Base(_args0)
+	return filepath.Base(_args0Name)
 }

+ 10 - 0
utils.go

@@ -18,9 +18,19 @@ func utilsIsSemanticVersion(version string) bool {
 	return semVerRegex.MatchString(version)
 }
 
+func utilsClenFileData(data string) (res string) {
+	res = utilsCheckAndRemoveBOM(data)
+	res = strings.Replace(res, "\r", "", -1)
+	res = strings.Split(res, "\n")[0]
+	res = strings.TrimSpace(res)
+	return res
+}
+
 func utilsClenFileDataMoreLine(data string) (res string) {
 	res = utilsCheckAndRemoveBOM(data)
 	res = strings.Replace(res, "\r", "", -1)
+	res = strings.TrimRight(res, "\n")
+	res = strings.TrimSpace(res)
 	return res
 }
 

+ 166 - 0
utils_test.go

@@ -0,0 +1,166 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package resource
+
+import "testing"
+
+func TestSemanticVersion(t *testing.T) {
+	if !utilsIsSemanticVersion("0.0.0") {
+		t.Errorf("SemanticVersion test failed: 0.0.0 (must be true, but return false)")
+	}
+
+	if !utilsIsSemanticVersion("1.0.0") {
+		t.Errorf("SemanticVersion test failed: 1.0.0 (must be true, but return false)")
+	}
+
+	if !utilsIsSemanticVersion("1.2.3") {
+		t.Errorf("SemanticVersion test failed: 1.2.3 (must be true, but return false)")
+	}
+
+	if !utilsIsSemanticVersion("1.0.0+dev") {
+		t.Errorf("SemanticVersion test failed: 1.0.0+dev (must be true, but return false)")
+	}
+
+	if !utilsIsSemanticVersion("1.0.0+dev-123") {
+		t.Errorf("SemanticVersion test failed: 1.0.0+dev-123 (must be true, but return false)")
+	}
+
+	if !utilsIsSemanticVersion("1.0.0+dev.123") {
+		t.Errorf("SemanticVersion test failed: 1.0.0+dev-123 (must be true, but return false)")
+	}
+
+	if !utilsIsSemanticVersion("1.0.0+dev-123.abc") {
+		t.Errorf("SemanticVersion test failed: 1.0.0+dev-123-456 (must be true, but return false)")
+	}
+
+	if !utilsIsSemanticVersion("1.0.0-123.456") {
+		t.Errorf("SemanticVersion test failed: 1.0.0-123-456 (must be true, but return false)")
+	}
+
+	if !utilsIsSemanticVersion("1.0.0-123-456+dev") {
+		t.Errorf("SemanticVersion test failed: 1.0.0-123-456+dev (must be true, but return false)")
+	}
+
+	if !utilsIsSemanticVersion("1.0.0-123-456+dev-127") {
+		t.Errorf("SemanticVersion test failed: 1.0.0-123-456+dev-127 (must be true, but return false)")
+	}
+
+	if utilsIsSemanticVersion("v0.0.0") {
+		t.Errorf("SemanticVersion test failed: v0.0.0 (must be false, but return true)")
+	}
+
+	if utilsIsSemanticVersion("1.0.0.0") {
+		t.Errorf("SemanticVersion test failed: 1.0.0.0 (must be false, but return true)")
+	}
+
+	if utilsIsSemanticVersion("1.0.0-123+dev-234+prod") {
+		t.Errorf("SemanticVersion test failed: 1.0.0-123+dev-234+prod (must be false, but return true)")
+	}
+}
+
+func TestCheckAndRemoveBOM(t *testing.T) {
+	hasBOM := string([]byte{0xEF, 0xBB, 0xBF}) + "Hello"
+	noBOM := "Hello"
+
+	if utilsCheckAndRemoveBOM(noBOM) != noBOM {
+		t.Errorf("No BOM check error")
+	}
+
+	if utilsCheckAndRemoveBOM(hasBOM) == hasBOM {
+		t.Errorf("Has BOM check error")
+	}
+
+	if utilsCheckAndRemoveBOM(hasBOM) != noBOM {
+		t.Errorf("Has BOM remove error")
+	}
+}
+
+func TestClenFileData(t *testing.T) {
+	t.Run("Text-OnlyLine", func(t *testing.T) {
+		text := "Hello"
+		if utilsClenFileData(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-OnlyLine-WithSpace", func(t *testing.T) {
+		text := "Hello    "
+		if utilsClenFileData(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-With-CRLF", func(t *testing.T) {
+		text := "Hello\r\n"
+		if utilsClenFileData(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-With-More-CRLF", func(t *testing.T) {
+		text := "Hello\r\n\r\n\r\n"
+		if utilsClenFileData(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-With-CRLF-WithSpace", func(t *testing.T) {
+		text := "Hello    \r\n"
+		if utilsClenFileData(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-MoreLine", func(t *testing.T) {
+		text := "Hello\r\nWorld"
+		if utilsClenFileData(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+}
+
+func TestClenFileDataMoreLine(t *testing.T) {
+	t.Run("Text-OnlyLine", func(t *testing.T) {
+		text := "Hello"
+		if utilsClenFileDataMoreLine(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-OnlyLine-WithSpace", func(t *testing.T) {
+		text := " Hello    "
+		if utilsClenFileDataMoreLine(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-With-CRLF", func(t *testing.T) {
+		text := "Hello\r\n"
+		if utilsClenFileDataMoreLine(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-With-More-CRLF", func(t *testing.T) {
+		text := "Hello\r\n\r\n\r\n"
+		if utilsClenFileDataMoreLine(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-With-CRLF-WithSpace", func(t *testing.T) {
+		text := "Hello    \r\n"
+		if utilsClenFileDataMoreLine(text) != "Hello" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+
+	t.Run("Text-MoreLine", func(t *testing.T) {
+		text := "Hello\r\nWorld"
+		if utilsClenFileDataMoreLine(text) != "Hello\nWorld" {
+			t.Errorf("ClenFileData OnlyLine error")
+		}
+	})
+}