// Package recruitment 的链接评分规则单元测试。
//
// 用贵阳市人社局「人事招考」列表页的真实锚点样本做回归,防止评分规则
// 调整后再次出现「导航页被误抓」或「真实公告被漏抓」两类问题。
package recruitment
import "testing"
// TestScoreAnchor 覆盖真实列表页中的典型锚点形态。
func TestScoreAnchor(t *testing.T) {
incWords := defaultIncludeWords
excWords := defaultExcludeWords
minScore := defaultMinScore
cases := []struct {
name string
a anchor
want bool // true=应保留(公告正文),false=应过滤(导航/栏目页)
}{
// ---------- 应保留:真实公告正文 ----------
{
name: "公告正文-带文件后缀",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/fdzdgklm/zfxxgkrsxx/rszk/202609/t20260908_90834079.html",
Text: "2026年贵阳贵安面向退役军人选拔培养中小学教师公告",
},
want: true,
},
{
name: "公告正文-标题不含标准词但URL是正文",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/fdzdgklm/zfxxgkrsxx/rszk/202603/t20260303_89596144.html",
Text: "贵阳贵安2026年公开招聘事业单位工作人员岗位取消情况公告",
},
want: true,
},
{
name: "公告正文-仅URL可判定",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/fdzdgklm/zfxxgkrsxx/rszk/202512/t20251225_89089652.html",
Text: "关于做好2026年度考试录用公务员工作的通知",
},
want: true,
},
// ---------- 应过滤:栏目目录页(以 / 结尾) ----------
{
name: "栏目页-人事招考",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/fdzdgklm/zfxxgkrsxx/rszk/",
Text: "人事招考",
},
want: false,
},
{
name: "栏目页-重点领域公开",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/fdzdgklm/fdzdgklmzdlygk/",
Text: "重点领域公开",
},
want: false,
},
{
name: "栏目页-政府非公开信息目录",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/fdzdgklm/zffgkxxml/",
Text: "政府非公开信息目录",
},
want: false,
},
{
name: "栏目页-建议提案公开",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/fdzdgklm/jytagk/",
Text: "建议提案公开",
},
want: false,
},
{
name: "栏目页-依申请公开",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/ysqgk/index.html",
Text: "依申请公开",
},
want: false,
},
{
name: "导航页-政府信息公开制度",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/zfxxgkzd/",
Text: "政府信息 公开制度",
},
want: false,
},
{
name: "导航页-政府信息公开年报",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/zfxxgknb/",
Text: "政府信息 公开年报",
},
want: false,
},
{
name: "导航页-返回首页",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/",
Text: "返回首页",
},
want: false,
},
{
name: "导航页-机构概况",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/fdzdgklm/zfxxgkjggk/",
Text: "机构概况",
},
want: false,
},
// ---------- 应过滤:友链/外部站点(URL 以 .html 结尾易误判) ----------
{
name: "友链-贵阳人才网",
a: anchor{
URL: "https://www.gyrc.cn/jobPosition/result.html",
Text: "贵阳人才网 - 贵阳市人才服务中心官方网站",
},
want: false,
},
{
name: "友链-信用中国",
a: anchor{
URL: "http://cx.guiyang.gov.cn/index.html",
Text: "信用中国(贵州贵阳)",
},
want: false,
},
{
name: "工具页-依申请公开",
a: anchor{
URL: "http://rsj.guiyang.gov.cn/zfxxgk/ysqgk/index.html",
Text: "贵阳市人力资源和社会保障局- 依申请公开",
},
want: false,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := scoreAnchor(c.a, incWords, excWords, minScore)
if got != c.want {
t.Errorf("scoreAnchor() = %v, want %v\n url=%s\n text=%s",
got, c.want, c.a.URL, c.a.Text)
}
})
}
}
// TestCleanText 验证标题清洗:去除标签与折叠空白。
func TestCleanText(t *testing.T) {
cases := []struct{ in, want string }{
{"\n\t\t\t人事招考", "人事招考"},
{"贵阳贵安 2026 年招聘公告", "贵阳贵安 2026 年招聘公告"},
{" 前后空白 ", "前后空白"},
}
for _, c := range cases {
if got := cleanText(c.in); got != c.want {
t.Errorf("cleanText(%q) = %q, want %q", c.in, got, c.want)
}
}
}
// TestIsImageFileName 验证图片文件名识别(用于剔除图片型锚点噪声)。
func TestIsImageFileName(t *testing.T) {
yes := []string{"ysqgk3.png", "banner.jpg", "logo.gif", "icon.SVG", "pic.webp"}
no := []string{"", "2026年招聘公告", "index.html", "关于招聘的通知"}
for _, s := range yes {
if !isImageFileName(s) {
t.Errorf("isImageFileName(%q) = false, want true", s)
}
}
for _, s := range no {
if isImageFileName(s) {
t.Errorf("isImageFileName(%q) = true, want false", s)
}
}
}
// TestExtractAnchorsImageFallback 验证图片型锚点不会以「文件名」形式进入候选。
// 复现真实页面:
func TestExtractAnchorsImageFallback(t *testing.T) {
html := `
`
got := extractAnchors(html, "http://rsj.guiyang.gov.cn/")
for _, a := range got {
if isImageFileName(a.Text) {
t.Errorf("图片型锚点应以文件名泄漏进候选: text=%q url=%s", a.Text, a.URL)
}
}
// 该锚点应被完全丢弃(无可读文本)。
if len(got) != 0 {
t.Errorf("期望丢弃图片型锚点,实际保留 %d 个: %+v", len(got), got)
}
}