[AI生成] go基于开源库实现url path匹配

package main

import (
	"fmt"

	"github.com/bmatcuk/doublestar/v4"
)

// ============================================================
// 测试框架与用例定义
// ============================================================

// testCase 单个测试用例
type testCase struct {
	name     string
	pattern  string
	path     string
	expected bool
}

// testGroup 一组测试
type testGroup struct {
	title string
	cases []testCase
}

// runTests 运行一组测试,输出结果并统计
func runTests(groups []testGroup) (passed, failed int) {
	for _, g := range groups {
		fmt.Printf("\n=== %s ===\n", g.title)
		for i, c := range g.cases {
			matched, err := doublestar.PathMatch(c.pattern, c.path)
			status := "PASS"
			if err != nil {
				status = fmt.Sprintf("ERROR: %v", err)
			} else if matched != c.expected {
				status = "FAIL"
			}
			fmt.Printf("  [%02d] %-25s pattern=%-28s path=%-32s expected=%-5v got=%-5v → %s\n",
				i+1, c.name, c.pattern, c.path, c.expected, matched, status)
			if status != "PASS" {
				failed++
			} else {
				passed++
			}
		}
	}
	return
}

// ============================================================
// main —— 定义测试用例并执行
// ============================================================

func main() {
	fmt.Println("============================================================")
	fmt.Println("  URL Path Matching Tests (doublestar.PathMatch only)")
	fmt.Println("============================================================")

	groups := []testGroup{
		// 1. 基础单级通配 *
		{
			title: "1. 基础单级通配符 *",
			cases: []testCase{
				{"API 路由单级", "/api/v1/users/*", "/api/v1/users/123", true},
				{"不跨多级目录", "/api/v1/users/*", "/api/v1/users/123/details", false},
				{"静态文件后缀", "/static/*.css", "/static/style.css", true},
				{"子目录不匹配", "/static/*.css", "/static/css/style.css", false},
				{"根一级", "/*", "/hello", true},
				{"根一级不跨级", "/*", "/a/b", false},
				{"空模式不匹配", "", "/api", false},
			},
		},

		// 2. 递归通配 **
		{
			title: "2. 递归通配符 **",
			cases: []testCase{
				{"任意深度 1", "/api/**", "/api/v1/users", true},
				{"任意深度 2", "/api/**", "/api/v1/x/y/z", true},
				{"包含自身", "/api/**", "/api", true},
				{"中间插入", "/a/**/z", "/a/b/c/z", true},
				{"中间跳过", "/a/**/z", "/a/z", true},
				{"后缀过滤", "/api/**/*.json", "/api/v1/data/users.json", true},
				{"后缀不匹配", "/api/**/*.json", "/api/v1/data/users.xml", false},
			},
		},

		// 3. 单字符通配 ?
		{
			title: "3. 单字符通配符 ?",
			cases: []testCase{
				{"版本一位", "/api/v?/users", "/api/v1/users", true},
				{"版本一位 2", "/api/v?/users", "/api/v2/users", true},
				{"版本两位不匹配", "/api/v?/users", "/api/v12/users", false},
				{"文件名单字符", "/img/logo?.png", "/img/logo1.png", true},
				{"文件名多字符", "/img/logo?.png", "/img/logo10.png", false},
			},
		},

		// 4. 字符类 []
		{
			title: "4. 字符类 [abc] [a-z] [0-9]",
			cases: []testCase{
				{"字符集合匹配", "/api/v[123]/users", "/api/v1/users", true},
				{"字符集合不匹配", "/api/v[123]/users", "/api/v4/users", false},
				{"数字范围", "/api/v[0-9]/users", "/api/v5/users", true},
				{"字母范围", "/[a-z]bc", "/abc", true},
				{"字母大小写敏感", "/[a-z]bc", "/Abc", false},
			},
		},

		// 5. 交替匹配 {}
		{
			title: "5. 交替匹配 {a,b,c}",
			cases: []testCase{
				{"API 版本交替", "/api/{v1,v2}/users", "/api/v1/users", true},
				{"API 版本交替 2", "/api/{v1,v2}/users", "/api/v2/users", true},
				{"API 版本交替不匹配", "/api/{v1,v2}/users", "/api/v3/users", false},
				{"扩展名交替", "/file.{json,yaml,yml}", "/file.json", true},
				{"扩展名交替 2", "/file.{json,yaml,yml}", "/file.yaml", true},
				{"扩展名不匹配", "/file.{json,yaml,yml}", "/file.xml", false},
			},
		},

		// 6. 组合模式
		{
			title: "6. 组合模式 (**, *, {}, [] 混合)",
			cases: []testCase{
				{"版本 + 递归 + 单级", "/api/v1/**/*", "/api/v1/users/123/details", true},
				{"静态资源白名单", "/static/**/*.{css,js,png,jpg}", "/static/css/main.css", true},
				{"静态资源白名单 2", "/static/**/*.{css,js,png,jpg}", "/static/img/logo.png", true},
				{"静态资源不匹配", "/static/**/*.{css,js,png,jpg}", "/static/data.xml", false},
				{"版本范围 + 递归 + 后缀", "/api/v[0-9]/**/*.json", "/api/v2/users/list.json", true},
				{"前缀交替 + 递归", "/{api,internal}/**", "/api/v1/users", true},
				{"前缀交替 + 递归 2", "/{api,internal}/**", "/internal/config", true},
			},
		},

		// 7. 精确匹配
		{
			title: "7. 精确匹配(无通配符)",
			cases: []testCase{
				{"完全相等", "/api/v1/users", "/api/v1/users", true},
				{"不相等", "/api/v1/users", "/api/v2/users", false},
				{"根路径", "/", "/", true},
				{"末尾斜杠差异 1", "/api", "/api/", false},
				{"末尾斜杠差异 2", "/api/", "/api", false},
			},
		},

		// 8. 真实业务场景
		{
			title: "8. 真实业务场景(REST 路由)",
			cases: []testCase{
				{"REST 用户详情", "/users/*", "/users/123", true},
				{"REST 用户订单", "/users/*/orders", "/users/123/orders", true},
				{"REST 用户订单不匹配", "/users/*/orders", "/users/123", false},
				{"管理后台", "/admin/**", "/admin/users/list", true},
				{"健康检查端点", "/healthz", "/healthz", true},
				{"健康检查子路径", "/health/**", "/health/live", true},
				{"静态资源", "/assets/**", "/assets/js/app.js", true},
				{"静态资源(自身匹配)", "/assets/**", "/assets", true},
			},
		},
	}

	// 运行所有测试
	passed, failed := runTests(groups)

	// 汇总
	fmt.Println("\n============================================================")
	fmt.Printf("  汇总: passed=%d, failed=%d\n", passed, failed)
	if failed == 0 {
		fmt.Println("  ✓ 所有测试通过!")
	} else {
		fmt.Printf("  ✗ 有 %d 个测试失败!\n", failed)
	}
	fmt.Println("============================================================")

	// 典型使用模式参考
	fmt.Println("\n  典型使用模式参考:")
	fmt.Println("    /api/**                         匹配 /api 下所有子路径")
	fmt.Println("    /static/**/*.{css,js,png,jpg}   匹配特定静态资源")
	fmt.Println("    /users/**                       匹配 /users 下所有子路径")
	fmt.Println("    /api/v[0-9]/**                  匹配版本化 API")
	fmt.Println("    /healthz                        精确匹配健康检查端点")
}

 

posted on 2026-06-13 21:32  王景迁  阅读(1)  评论(0)    收藏  举报

导航