1 line
19 KiB
JSON
1 line
19 KiB
JSON
{"repo": "akagiyui/suki-chat", "files": {"internal/model/deepseek_test.go": {"52316d683c8da2972b3152751d8d27d00d2c24b1": {"t": 1785745633.8171291, "n": 2792, "h": "f9ca12e36c5dacc5", "c": "package model\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n)\n\n// 单元测试:用本地 httptest 伪造 OpenAI 兼容响应,校验请求构造与响应解析。\nfunc TestDeepSeekChatUnit(t *testing.T) {\n\tvar gotBody chatRequestWire\n\tsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.Header.Get(\"Authorization\") != \"Bearer test-key\" {\n\t\t\tt.Errorf(\"缺少正确的 Authorization 头: %s\", r.Header.Get(\"Authorization\"))\n\t\t}\n\t\tif r.URL.Path != \"/chat/completions\" {\n\t\t\tt.Errorf(\"路径不符: %s\", r.URL.Path)\n\t\t}\n\t\traw, _ := io.ReadAll(r.Body)\n\t\t_ = json.Unmarshal(raw, &gotBody)\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\t_, _ = io.WriteString(w, `{\n\t\t\t\"choices\":[{\"message\":{\"role\":\"assistant\",\"content\":\"你好\"},\"finish_reason\":\"stop\"}],\n\t\t\t\"usage\":{\"prompt_tokens\":10,\"completion_tokens\":3,\"total_tokens\":13}\n\t\t}`)\n\t}))\n\tdefer srv.Close()\n\n\tc := NewDeepSeekClient(\"test-key\", srv.URL)\n\tresp, err := c.Chat(context.Background(), ChatRequest{\n\t\tModel: \"deepseek-v4-flash\",\n\t\tMessages: []Message{{Role: RoleUser, Content: \"hi\"}},\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"Chat 失败: %v\", err)\n\t}\n\tif gotBody.Model != \"deepseek-v4-flash\" {\n\t\tt.Fatalf(\"请求 model 不符: %s\", gotBody.Model)\n\t}\n\tif gotBody.Stream {\n\t\tt.Fatal(\"非流式请求 stream 应为 false\")\n\t}\n\tif resp.Message.Content != \"你好\" || resp.Usage.TotalTokens != 13 {\n\t\tt.Fatalf(\"响应解析不符: %+v\", resp)\n\t}\n}\n\nfunc TestDeepSeekChatErrorStatus(t *testing.T) {\n\tsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusUnauthorized)\n\t\t_, _ = io.WriteString(w, `{\"error\":{\"message\":\"invalid key\"}}`)\n\t}))\n\tdefer srv.Close()\n\tc := NewDeepSeekClient(\"bad\", srv.URL)\n\tif _, err := c.Chat(context.Background(), ChatRequest{Model: \"m\", Messages: []Message{{Role: RoleUser, Content: \"x\"}}}); err == nil {\n\t\tt.Fatal(\"非 200 应返回错误\")\n\t}\n}\n\nfunc TestDeepSeekNoAPIKey(t *testing.T) {\n\tc := NewDeepSeekClient(\"\", \"https://api.deepseek.com\")\n\tif _, err := c.Chat(context.Background(), ChatRequest{Model: \"m\"}); err == nil {\n\t\tt.Fatal(\"无 key 应直接报错\")\n\t}\n}\n\n// 集成测试:真正调用 DeepSeek,仅在设置了 DEEPSEEK_API_KEY 时运行。\nfunc TestDeepSeekChatIntegration(t *testing.T) {\n\tkey := os.Getenv(\"DEEPSEEK_API_KEY\")\n\tif key == \"\" {\n\t\tt.Skip(\"未设置 DEEPSEEK_API_KEY,跳过集成测试\")\n\t}\n\tc := NewDeepSeekClient(key, \"https://api.deepseek.com\")\n\tresp, err := c.Chat(context.Background(), ChatRequest{\n\t\tModel: \"deepseek-v4-flash\",\n\t\tMessages: []Message{\n\t\t\t{Role: RoleSystem, Content: \"你只回答一个词。\"},\n\t\t\t{Role: RoleUser, Content: \"用中文说你好\"},\n\t\t},\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"集成调用失败: %v\", err)\n\t}\n\tif strings.TrimSpace(resp.Message.Content) == \"\" {\n\t\tt.Fatal(\"应返回非空内容\")\n\t}\n\tif resp.Usage.TotalTokens == 0 {\n\t\tt.Fatal(\"应返回 token 用量\")\n\t}\n\tt.Logf(\"DeepSeek 回复: %q, 用量: %+v\", resp.Message.Content, resp.Usage)\n}\n"}, "63828305c65f3273356a6e1b2602a82ff995d902": {"t": 1785755097.0858378, "n": 2792, "h": "f9ca12e36c5dacc5", "c": "package model\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n)\n\n// 单元测试:用本地 httptest 伪造 OpenAI 兼容响应,校验请求构造与响应解析。\nfunc TestDeepSeekChatUnit(t *testing.T) {\n\tvar gotBody chatRequestWire\n\tsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.Header.Get(\"Authorization\") != \"Bearer test-key\" {\n\t\t\tt.Errorf(\"缺少正确的 Authorization 头: %s\", r.Header.Get(\"Authorization\"))\n\t\t}\n\t\tif r.URL.Path != \"/chat/completions\" {\n\t\t\tt.Errorf(\"路径不符: %s\", r.URL.Path)\n\t\t}\n\t\traw, _ := io.ReadAll(r.Body)\n\t\t_ = json.Unmarshal(raw, &gotBody)\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\t_, _ = io.WriteString(w, `{\n\t\t\t\"choices\":[{\"message\":{\"role\":\"assistant\",\"content\":\"你好\"},\"finish_reason\":\"stop\"}],\n\t\t\t\"usage\":{\"prompt_tokens\":10,\"completion_tokens\":3,\"total_tokens\":13}\n\t\t}`)\n\t}))\n\tdefer srv.Close()\n\n\tc := NewDeepSeekClient(\"test-key\", srv.URL)\n\tresp, err := c.Chat(context.Background(), ChatRequest{\n\t\tModel: \"deepseek-v4-flash\",\n\t\tMessages: []Message{{Role: RoleUser, Content: \"hi\"}},\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"Chat 失败: %v\", err)\n\t}\n\tif gotBody.Model != \"deepseek-v4-flash\" {\n\t\tt.Fatalf(\"请求 model 不符: %s\", gotBody.Model)\n\t}\n\tif gotBody.Stream {\n\t\tt.Fatal(\"非流式请求 stream 应为 false\")\n\t}\n\tif resp.Message.Content != \"你好\" || resp.Usage.TotalTokens != 13 {\n\t\tt.Fatalf(\"响应解析不符: %+v\", resp)\n\t}\n}\n\nfunc TestDeepSeekChatErrorStatus(t *testing.T) {\n\tsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusUnauthorized)\n\t\t_, _ = io.WriteString(w, `{\"error\":{\"message\":\"invalid key\"}}`)\n\t}))\n\tdefer srv.Close()\n\tc := NewDeepSeekClient(\"bad\", srv.URL)\n\tif _, err := c.Chat(context.Background(), ChatRequest{Model: \"m\", Messages: []Message{{Role: RoleUser, Content: \"x\"}}}); err == nil {\n\t\tt.Fatal(\"非 200 应返回错误\")\n\t}\n}\n\nfunc TestDeepSeekNoAPIKey(t *testing.T) {\n\tc := NewDeepSeekClient(\"\", \"https://api.deepseek.com\")\n\tif _, err := c.Chat(context.Background(), ChatRequest{Model: \"m\"}); err == nil {\n\t\tt.Fatal(\"无 key 应直接报错\")\n\t}\n}\n\n// 集成测试:真正调用 DeepSeek,仅在设置了 DEEPSEEK_API_KEY 时运行。\nfunc TestDeepSeekChatIntegration(t *testing.T) {\n\tkey := os.Getenv(\"DEEPSEEK_API_KEY\")\n\tif key == \"\" {\n\t\tt.Skip(\"未设置 DEEPSEEK_API_KEY,跳过集成测试\")\n\t}\n\tc := NewDeepSeekClient(key, \"https://api.deepseek.com\")\n\tresp, err := c.Chat(context.Background(), ChatRequest{\n\t\tModel: \"deepseek-v4-flash\",\n\t\tMessages: []Message{\n\t\t\t{Role: RoleSystem, Content: \"你只回答一个词。\"},\n\t\t\t{Role: RoleUser, Content: \"用中文说你好\"},\n\t\t},\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"集成调用失败: %v\", err)\n\t}\n\tif strings.TrimSpace(resp.Message.Content) == \"\" {\n\t\tt.Fatal(\"应返回非空内容\")\n\t}\n\tif resp.Usage.TotalTokens == 0 {\n\t\tt.Fatal(\"应返回 token 用量\")\n\t}\n\tt.Logf(\"DeepSeek 回复: %q, 用量: %+v\", resp.Message.Content, resp.Usage)\n}\n"}}, ".github/workflows/build.yml": {"52316d683c8da2972b3152751d8d27d00d2c24b1": {"t": 1785747026.0131998, "n": 3226, "h": "a41672649d8538bb", "c": "name: Build and Publish\n\non:\n push:\n branches: [\"**\"]\n tags: [\"v*\"]\n pull_request:\n branches: [master]\n workflow_dispatch:\n\nenv:\n REGISTRY: ghcr.io\n IMAGE_NAME: ${{ github.repository }}\n\njobs:\n test:\n name: Test\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v6\n\n - name: Set up Go\n uses: actions/setup-go@v5\n with:\n go-version-file: go.mod\n cache: true\n\n - name: Go vet\n run: go vet ./...\n\n - name: Go test\n # ubuntu runner 自带 Docker,会一并跑沙箱集成测试;\n # DeepSeek 集成测试因未设 DEEPSEEK_API_KEY 自动跳过。\n run: go test ./...\n\n - name: Set up pnpm\n uses: pnpm/action-setup@v4\n with:\n version: 10.33.1 # package.json 在 frontend/ 而非仓库根,需显式指定版本\n\n - name: Set up Node\n uses: actions/setup-node@v4\n with:\n node-version: 24\n cache: pnpm\n cache-dependency-path: frontend/pnpm-lock.yaml\n\n - name: Install frontend deps\n working-directory: frontend\n run: pnpm install --frozen-lockfile\n\n - name: Build frontend\n # 必须先 build:tanstackRouter 插件在构建时生成 routeTree.gen.ts(被 gitignore),\n # 后续 tsc 类型检查依赖它。\n working-directory: frontend\n run: pnpm build\n\n - name: Lint frontend\n working-directory: frontend\n run: pnpm exec eslint .\n\n - name: Typecheck frontend\n working-directory: frontend\n run: pnpm exec tsc -b\n\n build:\n name: Build (${{ matrix.variant }})\n runs-on: ubuntu-latest\n needs: test\n permissions:\n contents: read\n packages: write\n strategy:\n matrix:\n include:\n - variant: all-in-one\n is_primary: true\n - variant: external-db\n is_primary: false\n fail-fast: false\n\n steps:\n - uses: actions/checkout@v6\n\n - name: Log in to GitHub Container Registry\n if: github.event_name != 'pull_request'\n uses: docker/login-action@v4\n with:\n registry: ${{ env.REGISTRY }}\n username: ${{ github.actor }}\n password: ${{ secrets.GITHUB_TOKEN }}\n\n - name: Extract Docker metadata\n id: meta\n uses: docker/metadata-action@v6\n with:\n images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n tags: |\n type=raw,value=${{ matrix.variant }}\n type=raw,value=latest,enable=${{ matrix.is_primary && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')) }}\n type=semver,pattern={{version}},enable=${{ matrix.is_primary }}\n type=sha,prefix=${{ matrix.variant }}-\n flavor: |\n latest=false\n\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@v4\n\n - name: Build and push image\n uses: docker/build-push-action@v7\n with:\n context: .\n file: Containerfile\n target: ${{ matrix.variant }}\n push: ${{ github.event_name != 'pull_request' }}\n tags: ${{ steps.meta.outputs.tags }}\n labels: ${{ steps.meta.outputs.labels }}\n cache-from: type=gha,scope=${{ matrix.variant }}\n cache-to: type=gha,mode=max,scope=${{ matrix.variant }}\n"}, "992eb24101ffa3024c1eabb521afd2a7dc3d1b8b": {"t": 1785755097.839814, "n": 3226, "h": "a41672649d8538bb", "c": "name: Build and Publish\n\non:\n push:\n branches: [\"**\"]\n tags: [\"v*\"]\n pull_request:\n branches: [master]\n workflow_dispatch:\n\nenv:\n REGISTRY: ghcr.io\n IMAGE_NAME: ${{ github.repository }}\n\njobs:\n test:\n name: Test\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v6\n\n - name: Set up Go\n uses: actions/setup-go@v5\n with:\n go-version-file: go.mod\n cache: true\n\n - name: Go vet\n run: go vet ./...\n\n - name: Go test\n # ubuntu runner 自带 Docker,会一并跑沙箱集成测试;\n # DeepSeek 集成测试因未设 DEEPSEEK_API_KEY 自动跳过。\n run: go test ./...\n\n - name: Set up pnpm\n uses: pnpm/action-setup@v4\n with:\n version: 10.33.1 # package.json 在 frontend/ 而非仓库根,需显式指定版本\n\n - name: Set up Node\n uses: actions/setup-node@v4\n with:\n node-version: 24\n cache: pnpm\n cache-dependency-path: frontend/pnpm-lock.yaml\n\n - name: Install frontend deps\n working-directory: frontend\n run: pnpm install --frozen-lockfile\n\n - name: Build frontend\n # 必须先 build:tanstackRouter 插件在构建时生成 routeTree.gen.ts(被 gitignore),\n # 后续 tsc 类型检查依赖它。\n working-directory: frontend\n run: pnpm build\n\n - name: Lint frontend\n working-directory: frontend\n run: pnpm exec eslint .\n\n - name: Typecheck frontend\n working-directory: frontend\n run: pnpm exec tsc -b\n\n build:\n name: Build (${{ matrix.variant }})\n runs-on: ubuntu-latest\n needs: test\n permissions:\n contents: read\n packages: write\n strategy:\n matrix:\n include:\n - variant: all-in-one\n is_primary: true\n - variant: external-db\n is_primary: false\n fail-fast: false\n\n steps:\n - uses: actions/checkout@v6\n\n - name: Log in to GitHub Container Registry\n if: github.event_name != 'pull_request'\n uses: docker/login-action@v4\n with:\n registry: ${{ env.REGISTRY }}\n username: ${{ github.actor }}\n password: ${{ secrets.GITHUB_TOKEN }}\n\n - name: Extract Docker metadata\n id: meta\n uses: docker/metadata-action@v6\n with:\n images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n tags: |\n type=raw,value=${{ matrix.variant }}\n type=raw,value=latest,enable=${{ matrix.is_primary && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')) }}\n type=semver,pattern={{version}},enable=${{ matrix.is_primary }}\n type=sha,prefix=${{ matrix.variant }}-\n flavor: |\n latest=false\n\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@v4\n\n - name: Build and push image\n uses: docker/build-push-action@v7\n with:\n context: .\n file: Containerfile\n target: ${{ matrix.variant }}\n push: ${{ github.event_name != 'pull_request' }}\n tags: ${{ steps.meta.outputs.tags }}\n labels: ${{ steps.meta.outputs.labels }}\n cache-from: type=gha,scope=${{ matrix.variant }}\n cache-to: type=gha,mode=max,scope=${{ matrix.variant }}\n"}, "63828305c65f3273356a6e1b2602a82ff995d902": {"t": 1785755099.1626847, "n": 2975, "h": "655b4ddc2ee7dde8", "c": "name: Build and Publish\n\non:\n push:\n branches: [\"**\"]\n tags: [\"v*\"]\n pull_request:\n branches: [master]\n workflow_dispatch:\n\nenv:\n REGISTRY: ghcr.io\n IMAGE_NAME: ${{ github.repository }}\n\njobs:\n test:\n name: Test\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v6\n\n - name: Set up Go\n uses: actions/setup-go@v5\n with:\n go-version-file: go.mod\n cache: true\n\n - name: Go vet\n run: go vet ./...\n\n - name: Go test\n # ubuntu runner 自带 Docker,会一并跑沙箱集成测试;\n # DeepSeek 集成测试因未设 DEEPSEEK_API_KEY 自动跳过。\n run: go test ./...\n\n - name: Set up pnpm\n uses: pnpm/action-setup@v4\n\n - name: Set up Node\n uses: actions/setup-node@v4\n with:\n node-version: 24\n cache: pnpm\n cache-dependency-path: frontend/pnpm-lock.yaml\n\n - name: Install frontend deps\n working-directory: frontend\n run: pnpm install --frozen-lockfile\n\n - name: Lint frontend\n working-directory: frontend\n run: pnpm exec eslint .\n\n - name: Typecheck & build frontend\n working-directory: frontend\n run: pnpm exec tsc -b && pnpm build\n\n build:\n name: Build (${{ matrix.variant }})\n runs-on: ubuntu-latest\n needs: test\n permissions:\n contents: read\n packages: write\n strategy:\n matrix:\n include:\n - variant: all-in-one\n is_primary: true\n - variant: external-db\n is_primary: false\n fail-fast: false\n\n steps:\n - uses: actions/checkout@v6\n\n - name: Log in to GitHub Container Registry\n if: github.event_name != 'pull_request'\n uses: docker/login-action@v4\n with:\n registry: ${{ env.REGISTRY }}\n username: ${{ github.actor }}\n password: ${{ secrets.GITHUB_TOKEN }}\n\n - name: Extract Docker metadata\n id: meta\n uses: docker/metadata-action@v6\n with:\n images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n tags: |\n type=raw,value=${{ matrix.variant }}\n type=raw,value=latest,enable=${{ matrix.is_primary && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')) }}\n type=semver,pattern={{version}},enable=${{ matrix.is_primary }}\n type=sha,prefix=${{ matrix.variant }}-\n flavor: |\n latest=false\n\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@v4\n\n - name: Build and push image\n uses: docker/build-push-action@v7\n with:\n context: .\n file: Containerfile\n target: ${{ matrix.variant }}\n push: ${{ github.event_name != 'pull_request' }}\n tags: ${{ steps.meta.outputs.tags }}\n labels: ${{ steps.meta.outputs.labels }}\n cache-from: type=gha,scope=${{ matrix.variant }}\n cache-to: type=gha,mode=max,scope=${{ matrix.variant }}\n"}, "7e6bfc217289efdb5b80bc2bc1aee7c067f553b8": {"t": 1785755100.5673332, "n": 1959, "h": "cef00929158431db", "c": "name: Build and Publish\n\non:\n push:\n branches: [\"**\"]\n tags: [\"v*\"]\n pull_request:\n branches: [master]\n workflow_dispatch:\n\nenv:\n REGISTRY: ghcr.io\n IMAGE_NAME: ${{ github.repository }}\n\njobs:\n build:\n name: Build (${{ matrix.variant }})\n runs-on: ubuntu-latest\n permissions:\n contents: read\n packages: write\n strategy:\n matrix:\n include:\n - variant: all-in-one\n is_primary: true\n - variant: external-db\n is_primary: false\n fail-fast: false\n\n steps:\n - uses: actions/checkout@v6\n\n - name: Log in to GitHub Container Registry\n if: github.event_name != 'pull_request'\n uses: docker/login-action@v4\n with:\n registry: ${{ env.REGISTRY }}\n username: ${{ github.actor }}\n password: ${{ secrets.GITHUB_TOKEN }}\n\n - name: Extract Docker metadata\n id: meta\n uses: docker/metadata-action@v6\n with:\n images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n tags: |\n type=raw,value=${{ matrix.variant }}\n type=raw,value=latest,enable=${{ matrix.is_primary && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')) }}\n type=semver,pattern={{version}},enable=${{ matrix.is_primary }}\n type=sha,prefix=${{ matrix.variant }}-\n flavor: |\n latest=false\n\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@v4\n\n - name: Build and push image\n uses: docker/build-push-action@v7\n with:\n context: .\n file: Containerfile\n target: ${{ matrix.variant }}\n push: ${{ github.event_name != 'pull_request' }}\n tags: ${{ steps.meta.outputs.tags }}\n labels: ${{ steps.meta.outputs.labels }}\n cache-from: type=gha,scope=${{ matrix.variant }}\n cache-to: type=gha,mode=max,scope=${{ matrix.variant }}\n"}}}} |