|
| 1 | +import { Octokit } from "@octokit/core"; |
| 2 | +import fetchMock from "fetch-mock"; |
| 3 | +import MockDate from "mockdate"; |
| 4 | + |
| 5 | +const APP_ID = 1; |
| 6 | +const PRIVATE_KEY = `-----BEGIN RSA PRIVATE KEY----- |
| 7 | +MIIEpAIBAAKCAQEA1c7+9z5Pad7OejecsQ0bu3aozN3tihPmljnnudb9G3HECdnH |
| 8 | +lWu2/a1gB9JW5TBQ+AVpum9Okx7KfqkfBKL9mcHgSL0yWMdjMfNOqNtrQqKlN4kE |
| 9 | +p6RD++7sGbzbfZ9arwrlD/HSDAWGdGGJTSOBM6pHehyLmSC3DJoR/CTu0vTGTWXQ |
| 10 | +rO64Z8tyXQPtVPb/YXrcUhbBp8i72b9Xky0fD6PkEebOy0Ip58XVAn2UPNlNOSPS |
| 11 | +ye+Qjtius0Md4Nie4+X8kwVI2Qjk3dSm0sw/720KJkdVDmrayeljtKBx6AtNQsSX |
| 12 | +gzQbeMmiqFFkwrG1+zx6E7H7jqIQ9B6bvWKXGwIDAQABAoIBAD8kBBPL6PPhAqUB |
| 13 | +K1r1/gycfDkUCQRP4DbZHt+458JlFHm8QL6VstKzkrp8mYDRhffY0WJnYJL98tr4 |
| 14 | +4tohsDbqFGwmw2mIaHjl24LuWXyyP4xpAGDpl9IcusjXBxLQLp2m4AKXbWpzb0OL |
| 15 | +Ulrfc1ZooPck2uz7xlMIZOtLlOPjLz2DuejVe24JcwwHzrQWKOfA11R/9e50DVse |
| 16 | +hnSH/w46Q763y4I0E3BIoUMsolEKzh2ydAAyzkgabGQBUuamZotNfvJoDXeCi1LD |
| 17 | +8yNCWyTlYpJZJDDXooBU5EAsCvhN1sSRoaXWrlMSDB7r/E+aQyKua4KONqvmoJuC |
| 18 | +21vSKeECgYEA7yW6wBkVoNhgXnk8XSZv3W+Q0xtdVpidJeNGBWnczlZrummt4xw3 |
| 19 | +xs6zV+rGUDy59yDkKwBKjMMa42Mni7T9Fx8+EKUuhVK3PVQyajoyQqFwT1GORJNz |
| 20 | +c/eYQ6VYOCSC8OyZmsBM2p+0D4FF2/abwSPMmy0NgyFLCUFVc3OECpkCgYEA5OAm |
| 21 | +I3wt5s+clg18qS7BKR2DuOFWrzNVcHYXhjx8vOSWV033Oy3yvdUBAhu9A1LUqpwy |
| 22 | +Ma+unIgxmvmUMQEdyHQMcgBsVs10dR/g2xGjMLcwj6kn+xr3JVIZnbRT50YuPhf+ |
| 23 | +ns1ScdhP6upo9I0/sRsIuN96Gb65JJx94gQ4k9MCgYBO5V6gA2aMQvZAFLUicgzT |
| 24 | +u/vGea+oYv7tQfaW0J8E/6PYwwaX93Y7Q3QNXCoCzJX5fsNnoFf36mIThGHGiHY6 |
| 25 | +y5bZPPWFDI3hUMa1Hu/35XS85kYOP6sGJjf4kTLyirEcNKJUWH7CXY+00cwvTkOC |
| 26 | +S4Iz64Aas8AilIhRZ1m3eQKBgQCUW1s9azQRxgeZGFrzC3R340LL530aCeta/6FW |
| 27 | +CQVOJ9nv84DLYohTVqvVowdNDTb+9Epw/JDxtDJ7Y0YU0cVtdxPOHcocJgdUGHrX |
| 28 | +ZcJjRIt8w8g/s4X6MhKasBYm9s3owALzCuJjGzUKcDHiO2DKu1xXAb0SzRcTzUCn |
| 29 | +7daCswKBgQDOYPZ2JGmhibqKjjLFm0qzpcQ6RPvPK1/7g0NInmjPMebP0K6eSPx0 |
| 30 | +9/49J6WTD++EajN7FhktUSYxukdWaCocAQJTDNYP0K88G4rtC2IYy5JFn9SWz5oh |
| 31 | +x//0u+zd/R/QRUzLOw4N72/Hu+UG6MNt5iDZFCtapRaKt6OvSBwy8w== |
| 32 | +-----END RSA PRIVATE KEY-----`; |
| 33 | +const CLIENT_ID = "0123"; |
| 34 | +const CLIENT_SECRET = "0123secret"; |
| 35 | +const WEBHOOK_SECRET = "secret"; |
| 36 | + |
| 37 | +import { App } from "../src/index.ts"; |
| 38 | + |
| 39 | +describe("app.getInstallationUrl", () => { |
| 40 | + let app: InstanceType<typeof App>; |
| 41 | + let mock: typeof fetchMock; |
| 42 | + |
| 43 | + beforeEach(() => { |
| 44 | + MockDate.set(0); |
| 45 | + mock = fetchMock.sandbox(); |
| 46 | + |
| 47 | + app = new App({ |
| 48 | + appId: APP_ID, |
| 49 | + privateKey: PRIVATE_KEY, |
| 50 | + oauth: { |
| 51 | + clientId: CLIENT_ID, |
| 52 | + clientSecret: CLIENT_SECRET, |
| 53 | + }, |
| 54 | + webhooks: { |
| 55 | + secret: WEBHOOK_SECRET, |
| 56 | + }, |
| 57 | + Octokit: Octokit.defaults({ |
| 58 | + request: { |
| 59 | + fetch: mock, |
| 60 | + }, |
| 61 | + }), |
| 62 | + }); |
| 63 | + }); |
| 64 | + |
| 65 | + test("throws when response is null", async () => { |
| 66 | + mock.getOnce("path:/app", { |
| 67 | + body: "null", |
| 68 | + headers: { "Content-Type": "application/json" }, |
| 69 | + }); |
| 70 | + |
| 71 | + await expect(app.getInstallationUrl()).rejects.toThrow( |
| 72 | + "[@octokit/app] unable to fetch metadata for app", |
| 73 | + ); |
| 74 | + |
| 75 | + expect(mock.done()).toBe(true); |
| 76 | + }); |
| 77 | + |
| 78 | + test("returns correct url", async () => { |
| 79 | + mock.getOnce("path:/app", { |
| 80 | + html_url: "https://github.com/apps/octokit", |
| 81 | + }); |
| 82 | + |
| 83 | + const url = await app.getInstallationUrl(); |
| 84 | + |
| 85 | + expect(url).toEqual("https://github.com/apps/octokit/installations/new"); |
| 86 | + expect(mock.done()).toBe(true); |
| 87 | + }); |
| 88 | + |
| 89 | + test("caches url", async () => { |
| 90 | + mock.getOnce("path:/app", { |
| 91 | + html_url: "https://github.com/apps/octokit", |
| 92 | + }); |
| 93 | + |
| 94 | + const urls = await Promise.all([ |
| 95 | + app.getInstallationUrl(), |
| 96 | + app.getInstallationUrl(), |
| 97 | + app.getInstallationUrl(), |
| 98 | + ]); |
| 99 | + |
| 100 | + expect(urls).toEqual( |
| 101 | + new Array(3).fill("https://github.com/apps/octokit/installations/new"), |
| 102 | + ); |
| 103 | + expect(mock.done()).toBe(true); |
| 104 | + }); |
| 105 | + |
| 106 | + test("does not cache state", async () => { |
| 107 | + mock.getOnce("path:/app", { |
| 108 | + html_url: "https://github.com/apps/octokit", |
| 109 | + }); |
| 110 | + const state = "abc123"; |
| 111 | + |
| 112 | + const urlWithoutState = await app.getInstallationUrl(); |
| 113 | + const urlWithState = await app.getInstallationUrl({ state }); |
| 114 | + |
| 115 | + expect(urlWithoutState).toEqual( |
| 116 | + "https://github.com/apps/octokit/installations/new", |
| 117 | + ); |
| 118 | + expect(urlWithState).toEqual( |
| 119 | + `https://github.com/apps/octokit/installations/new?state=${state}`, |
| 120 | + ); |
| 121 | + expect(mock.done()).toBe(true); |
| 122 | + }); |
| 123 | + |
| 124 | + test("adds the url-encoded state string to the url", async () => { |
| 125 | + mock.getOnce("path:/app", { |
| 126 | + html_url: "https://github.com/apps/octokit", |
| 127 | + }); |
| 128 | + const state = "abc123%/{"; |
| 129 | + |
| 130 | + const url = await app.getInstallationUrl({ state }); |
| 131 | + |
| 132 | + expect(url).toEqual( |
| 133 | + `https://github.com/apps/octokit/installations/new?state=${encodeURIComponent(state)}`, |
| 134 | + ); |
| 135 | + expect(mock.done()).toBe(true); |
| 136 | + }); |
| 137 | + |
| 138 | + test("appends /permissions to the url when target_id present", async () => { |
| 139 | + mock.getOnce("path:/app", { |
| 140 | + html_url: "https://github.com/apps/octokit", |
| 141 | + }); |
| 142 | + const target_id = 456; |
| 143 | + |
| 144 | + const url = await app.getInstallationUrl({ target_id }); |
| 145 | + |
| 146 | + expect(url).toEqual( |
| 147 | + `https://github.com/apps/octokit/installations/new/permissions?target_id=${target_id}`, |
| 148 | + ); |
| 149 | + expect(mock.done()).toBe(true); |
| 150 | + }); |
| 151 | + |
| 152 | + test("adds both state and target_id to the url", async () => { |
| 153 | + mock.getOnce("path:/app", { |
| 154 | + html_url: "https://github.com/apps/octokit", |
| 155 | + }); |
| 156 | + const state = "abc123"; |
| 157 | + const target_id = 456; |
| 158 | + |
| 159 | + const url = await app.getInstallationUrl({ state, target_id }); |
| 160 | + |
| 161 | + expect(url).toEqual( |
| 162 | + `https://github.com/apps/octokit/installations/new/permissions?target_id=${target_id}&state=${state}`, |
| 163 | + ); |
| 164 | + expect(mock.done()).toBe(true); |
| 165 | + }); |
| 166 | +}); |
0 commit comments