swadhinbiswas feat: complete DevPulse production developer operating system for Discord · 9/6/2026
4a8d707
97 lines · 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { describe, expect, it } from "vitest";
import { computeRepoDashboardMetrics } from "./analytics.js";
import type { GitHubCommit, GitHubIssue, GitHubPullRequest, GitHubRelease } from "./types.js";

describe("GitHub metrics computation", () => {
  it("calculates accurate activity, focus, and cycle time", () => {
    const now = Date.now();
    const oneDay = 24 * 60 * 60 * 1000;

    const mockCommits: GitHubCommit[] = [
      {
        sha: "abc1",
        message: "feat: add discord buttons",
        author: { name: "Alice", date: new Date(now - oneDay).toISOString() },
        htmlUrl: "https://github.com",
      },
      {
        sha: "abc2",
        message: "docs: update readme with setup guide",
        author: { name: "Alice", date: new Date(now - 2 * oneDay).toISOString() },
        htmlUrl: "https://github.com",
      },
      {
        sha: "abc3",
        message: "chore: bump dependencies",
        author: { name: "Bob", date: new Date(now - 3 * oneDay).toISOString() },
        htmlUrl: "https://github.com",
      },
    ];

    const mockPrs: GitHubPullRequest[] = [
      {
        id: 1,
        number: 101,
        title: "Feature A",
        state: "closed",
        htmlUrl: "https://github.com",
        createdAt: new Date(now - 2 * oneDay).toISOString(),
        updatedAt: new Date(now - oneDay).toISOString(),
        closedAt: new Date(now - oneDay).toISOString(),
        mergedAt: new Date(now - oneDay).toISOString(), // Merged in 1 day
        author: { login: "Alice", avatarUrl: "" },
        draft: false,
      },
    ];

    const mockIssues: GitHubIssue[] = [
      {
        id: 1,
        number: 50,
        title: "Bug in cache layer",
        state: "closed",
        htmlUrl: "https://github.com",
        createdAt: new Date(now - 3 * oneDay).toISOString(),
        closedAt: new Date(now - oneDay).toISOString(), // Closed in 2 days
        author: { login: "Charlie", avatarUrl: "" },
        commentsCount: 3,
        isPullRequest: false,
      },
    ];

    const mockReleases: GitHubRelease[] = [
      {
        id: 1,
        tagName: "v1.0.0",
        name: "Initial Release",
        publishedAt: new Date(now - 5 * oneDay).toISOString(),
        htmlUrl: "https://github.com",
        body: "Release notes",
        prerelease: false,
      },
    ];

    const metrics = computeRepoDashboardMetrics(mockCommits, mockPrs, mockIssues, mockReleases);

    expect(metrics.activity.commitsCount).toBe(3);
    expect(metrics.activity.prsMerged).toBe(1);
    expect(metrics.activity.issuesClosed).toBe(1);
    expect(metrics.activity.releasesCount).toBe(1);

    // Verify cycle time calculations
    expect(metrics.cycleTime.avgTimeToMergeMs).toBeGreaterThan(0);
    expect(metrics.cycleTime.avgTimeToMergeMs).toBeLessThanOrEqual(oneDay + 1000);
    expect(metrics.cycleTime.avgIssueCycleTimeMs).toBeGreaterThan(0);

    // Verify focus breakdown percentages sum roughly to 100
    const totalFocus =
      metrics.focus.codingPercent +
      metrics.focus.reviewsPercent +
      metrics.focus.issuesPercent +
      metrics.focus.docsPercent +
      metrics.focus.maintenancePercent;
    expect(totalFocus).toBeGreaterThanOrEqual(95);
    expect(totalFocus).toBeLessThanOrEqual(105);
  });
});