• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

willmendesneto / node-github-diff / #261

31 Oct 2023 11:05PM UTC coverage: 62.667% (-0.2%) from 62.857%
#261

Pull #37

willmendesneto
feat: adding `repository` param validation
Pull Request #37: feat: adding `repository` param validation

11 of 20 branches covered (0.0%)

Branch coverage included in aggregate %.

10 of 10 new or added lines in 1 file covered. (100.0%)

36 of 55 relevant lines covered (65.45%)

2.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.67
/index.js
1
const { Octokit } = require('@octokit/rest');
1✔
2
const isBinary = require('is-binary-buffer');
1✔
3

4
const atob = (base64encoded) => {
1✔
5
  const decodedFile = Buffer.from(base64encoded, 'base64');
12✔
6

7
  return isBinary(decodedFile) ? decodedFile : decodedFile.toString('utf8');
12!
8
};
9

10
const buildHeader = (fileA, fileB) => `diff --git a/${fileA} b/${fileB}\n` + `--- a/${fileA}\n` + `+++ b/${fileB}\n`;
7✔
11

12
const getContent = async (github, owner, repo, path, commit) => {
1✔
13
  try {
12✔
14
    const { data } = await github.repos.getContent({
12✔
15
      owner,
16
      repo,
17
      path,
18
      ref: commit,
19
    });
20

21
    return data.content;
12✔
22
  } catch (err) {
23
    try {
×
24
      const apiError = JSON.parse(err);
×
25
      if (apiError.errors.find((error) => error.code === 'too_large')) {
×
26
        const gitTree = await github.gitdata.getTree({
×
27
          owner,
28
          repo,
29
          // More details why recursive is required in
30
          // https://developer.github.com/v3/git/trees/#get-a-tree-recursively
31
          recursive: true,
32
          sha: commit,
33
        });
34

35
        const { sha } = gitTree.tree.find((file) => file.path === path) || {};
×
36
        const { content } = await github.gitdata.getBlob({
×
37
          owner,
38
          repo,
39
          sha,
40
        });
41

42
        return content;
×
43
      }
44

45
      throw err;
×
46
    } catch (parseError) {
47
      throw new Error(`Unable to get content for ${path} @commit:${commit}. ${err}`);
×
48
    }
49
  }
50
};
51

52
const getContentByStatus = async ({ github, owner, repo, base, head, file }) => {
1✔
53
  const { filename, patch, status } = file;
7✔
54
  let content;
55
  // Get the content for the files
56
  if (status === 'removed') {
7✔
57
    content = await getContent(github, owner, repo, filename, base);
1✔
58

59
    return {
1✔
60
      filename,
61
      patch,
62
      status,
63
      header: buildHeader(filename, filename),
64
      fileA: atob(content),
65
    };
66
  } else if (status === 'added') {
6✔
67
    content = await getContent(github, owner, repo, filename, head);
1✔
68

69
    return {
1✔
70
      filename,
71
      patch,
72
      status,
73
      header: buildHeader(filename, filename),
74
      fileB: atob(content),
75
    };
76
  } else if (status === 'modified') {
5!
77
    const [fileA, fileB] = await Promise.all([
5✔
78
      getContent(github, owner, repo, filename, base),
79
      getContent(github, owner, repo, filename, head),
80
    ]);
81

82
    return {
5✔
83
      filename,
84
      patch,
85
      status,
86
      header: buildHeader(filename, filename),
87
      fileA: atob(fileA),
88
      fileB: atob(fileB),
89
    };
90
  } else if (status === 'renamed') {
×
91
    content = await getContent(github, owner, repo, filename, head);
×
92
    const decodedFile = atob(content);
×
93
    const previousFilename = file.previous_filename;
×
94
    const header = buildHeader(filename, previousFilename);
×
95

96
    return {
×
97
      filename,
98
      patch,
99
      status,
100
      header,
101
      previousFilename,
102
      fileA: decodedFile,
103
      fileB: decodedFile,
104
    };
105
  }
106

107
  return {
×
108
    filename,
109
    patch,
110
    status,
111
    header: buildHeader(filename, filename),
112
  };
113
};
114

115
const comparePackageVersions = async (github, owner, repo, base, head) => {
1✔
116
  try {
1✔
117
    const { data } = await github.repos.compareCommits({
1✔
118
      owner,
119
      repo,
120
      base,
121
      head,
122
    });
123

124
    const commits = await Promise.all(
1✔
125
      data.files.map((file) => {
126
        const content = {
7✔
127
          github,
128
          owner,
129
          repo,
130
          base,
131
          head,
132
          file,
133
        };
134

135
        return getContentByStatus(content);
7✔
136
      })
137
    );
138

139
    return commits;
1✔
140
  } catch (err) {
141
    throw new Error(`Unable to access the github repository for '${repo}'. ${err}`);
×
142
  }
143
};
144

145
const nodeGithubDiff = async ({ repository, base, head, githubToken }) => {
1✔
146
  try {
1✔
147
    // Setup the github api
148
    const github = new Octokit({
1✔
149
      auth: githubToken || undefined,
2✔
150
      baseUrl: 'https://api.github.com',
151
      userAgent: 'node-github-diff',
152
      request: {
153
        agent: false,
154
        timeout: 0,
155
      },
156
    });
157

158
    const [owner, repo] = repository.split('/');
1✔
159
    if (!owner || !repo) {
1!
160
      throw new Error('Repository param should have "owner/repo" pattern');
×
161
    }
162

163
    const data = await comparePackageVersions(github, owner, repo, base, head);
1✔
164

165
    return data;
1✔
166
  } catch (error) {
167
    throw error;
×
168
  }
169
};
170

171
module.exports = nodeGithubDiff;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc