Skip to content

Commit

Permalink
Merge pull request #1783 from yuvipanda/somemoretypes
Browse files Browse the repository at this point in the history
JS: Fix some JSDoc types, and misc details in tests
  • Loading branch information
consideRatio authored Oct 22, 2023
2 parents 61fc931 + bcae809 commit b2e2ed6
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 84 deletions.
40 changes: 20 additions & 20 deletions js/packages/binderhub-client/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export class BinderRepository {
*
* @param {string} providerSpec Spec of the form <provider>/<repo>/<ref> to pass to the binderhub API.
* @param {URL} buildEndpointUrl API URL of the build endpoint to talk to
* @param {string} buildToken Optional JWT based build token if this binderhub installation requires using build tokens
* @param {boolean} buildOnly Opt out of launching built image by default by passing `build_only` param
* @param {string} [buildToken] Optional JWT based build token if this binderhub installation requires using build tokens
* @param {boolean} [buildOnly] Opt out of launching built image by default by passing `build_only` param
*/
constructor(providerSpec, buildEndpointUrl, buildToken, buildOnly) {
this.providerSpec = providerSpec;
Expand Down Expand Up @@ -47,24 +47,24 @@ export class BinderRepository {
/**
* Call the binderhub API and yield responses as they come in
*
* Returns an Async Generator yielding each item returned by the
* Returns an Async iterator yielding each item returned by the
* server API.
*
* @typedef Line
* @prop {[string]} phase The phase the build is currently in. One of: building, built, fetching, launching, ready, unknown, waiting
* @prop {[string]} message Human readable message to display to the user. Extra newlines must *not* be added
* @prop {[string]} imageName (only with built) Full name of the image that has been built
* @prop {[string]} binder_launch_host (only with phase=ready) The host this binderhub API request was serviced by.
* @prop {string} [phase] The phase the build is currently in. One of: building, built, fetching, launching, ready, unknown, waiting
* @prop {string} [message] Human readable message to display to the user. Extra newlines must *not* be added
* @prop {string} [imageName] (only with built) Full name of the image that has been built
* @prop {string} [binder_launch_host] (only with phase=ready) The host this binderhub API request was serviced by.
* Could be different than the host the request was made to in federated cases
* @prop {[string]} binder_request (only with phase=ready) Request used to construct this image, of form v2/<provider>/<repo>/<ref>
* @prop {[string]} binder_persistent_request (only with phase=ready) Same as binder_request, but <ref> is fully resolved
* @prop {[string]} binder_ref_url (only with phase=ready) A URL to the repo provider where the repo can be browsed
* @prop {[string]} image (only with phase=ready) Full name of the image that has been built
* @prop {[string]} token (only with phase=ready) Token to use to authenticate with jupyter server at url
* @prop {[string]} url (only with phase=ready) URL where a jupyter server has been started
* @prop {[string]} repo_url (only with phase=ready) URL of the repository that is ready to be launched
* @prop {string} [binder_request] (only with phase=ready) Request used to construct this image, of form v2/<provider>/<repo>/<ref>
* @prop {string} [binder_persistent_request] (only with phase=ready) Same as binder_request, but <ref> is fully resolved
* @prop {string} [binder_ref_url] (only with phase=ready) A URL to the repo provider where the repo can be browsed
* @prop {string} [image] (only with phase=ready) Full name of the image that has been built
* @prop {string} [token] (only with phase=ready) Token to use to authenticate with jupyter server at url
* @prop {string} [url] (only with phase=ready) URL where a jupyter server has been started
* @prop {string} [repo_url] (only with phase=ready) URL of the repository that is ready to be launched
*
* @returns {AsyncGenerator<Line>} An async generator yielding responses from the API as they come in
* @returns {AsyncIterable<Line>} An async iterator yielding responses from the API as they come in
*/
fetch() {
this.eventSource = new EventSource(this.buildUrl);
Expand Down Expand Up @@ -109,8 +109,8 @@ export class BinderRepository {
* @param {URL} serverUrl URL to the running jupyter server
* @param {string} token Secret token used to authenticate to the jupyter server
* @param {string} path The path of the file or url suffix to launch the user into
* @param {string} pathType One of "lab", "file" or "url", denoting what kinda path we are launching the user into
* @param {string} [path] The path of the file or url suffix to launch the user into
* @param {string} [pathType] One of "lab", "file" or "url", denoting what kinda path we are launching the user into
*
* @returns {URL} A URL to redirect the user to
*/
Expand Down Expand Up @@ -155,10 +155,10 @@ export class BinderRepository {
*
* @param {URL} publicBaseUrl Base URL to use for making public URLs. Must end with a trailing slash.
* @param {string} providerPrefix prefix denoting what provider was selected
* @param {string} repo repo to build
* @param {string} repository repo to build
* @param {string} ref optional ref in this repo to build
* @param {[string]} path Path to launch after this repo has been built
* @param {[string]} pathType Type of thing to open path with (raw url, notebook file, lab, etc)
* @param {string} [path] Path to launch after this repo has been built
* @param {string} [pathType] Type of thing to open path with (raw url, notebook file, lab, etc)
*
* @returns {URL} A URL that can be shared with others, and clicking which will launch the repo
*/
Expand Down
119 changes: 57 additions & 62 deletions js/packages/binderhub-client/tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
makeBadgeMarkup,
} from "@jupyterhub/binderhub-client";
import { parseEventSource, simpleEventSourceServer } from "./utils";
import fs from "node:fs";
import { readFileSync } from "node:fs";

test("Passed in URL object is not modified", () => {
const buildEndpointUrl = new URL("https://test-binder.org/build");
Expand Down Expand Up @@ -59,7 +59,10 @@ test("Get full redirect URL with correct token but no path", () => {
);
expect(
br
.getFullRedirectURL("https://hub.test-binder.org/user/something", "token")
.getFullRedirectURL(
new URL("https://hub.test-binder.org/user/something"),
"token",
)
.toString(),
).toBe("https://hub.test-binder.org/user/something?token=token");
});
Expand All @@ -72,7 +75,7 @@ test("Get full redirect URL with urlpath", () => {
expect(
br
.getFullRedirectURL(
"https://hub.test-binder.org/user/something",
new URL("https://hub.test-binder.org/user/something"),
"token",
"rstudio",
"url",
Expand All @@ -89,7 +92,7 @@ test("Get full redirect URL when opening a file with jupyterlab", () => {
expect(
br
.getFullRedirectURL(
"https://hub.test-binder.org/user/something",
new URL("https://hub.test-binder.org/user/something"),
"token",
"index.ipynb",
"lab",
Expand All @@ -108,7 +111,7 @@ test("Get full redirect URL when opening a file with classic notebook (with file
expect(
br
.getFullRedirectURL(
"https://hub.test-binder.org/user/something",
new URL("https://hub.test-binder.org/user/something"),
"token",
"index.ipynb",
"file",
Expand All @@ -128,7 +131,7 @@ test("Get full redirect URL and deal with excessive slashes (with pathType=url)"
br
.getFullRedirectURL(
// Trailing slash should not be preserved here
"https://hub.test-binder.org/user/something/",
new URL("https://hub.test-binder.org/user/something/"),
"token",
// Trailing slash should be preserved here, but leading slash should not be repeated
"/rstudio/",
Expand All @@ -146,7 +149,7 @@ test("Get full redirect URL and deal with excessive slashes (with pathType=lab)"
expect(
br
.getFullRedirectURL(
"https://hub.test-binder.org/user/something/",
new URL("https://hub.test-binder.org/user/something/"),
"token",
// Both leading and trailing slashes should be gone here.
"/directory/index.ipynb/",
Expand All @@ -167,7 +170,7 @@ test("Get full redirect URL and deal with missing trailing slash", () => {
br
.getFullRedirectURL(
// Missing trailing slash here should not affect target url
"https://hub.test-binder.org/user/something",
new URL("https://hub.test-binder.org/user/something"),
"token",
"/directory/index.ipynb/",
"lab",
Expand All @@ -186,7 +189,7 @@ test("Get full redirect URL and deal with excessive slashes (with pathType=file)
expect(
br
.getFullRedirectURL(
"https://hub.test-binder.org/user/something/",
new URL("https://hub.test-binder.org/user/something/"),
"token",
// Both leading and trailing slashes should be gone here.
"/directory/index.ipynb/",
Expand All @@ -198,65 +201,57 @@ test("Get full redirect URL and deal with excessive slashes (with pathType=file)
);
});

describe(
"Iterate over full output from calling the binderhub API",
() => {
let closeServer, serverUrl;
describe("Iterate over full output from calling the binderhub API", () => {
let closeServer, serverUrl;

let responseContents = fs.readFileSync(
`${__dirname}/fixtures/fullbuild.eventsource`,
{ encoding: "utf-8" },
);
beforeEach(async () => {
[serverUrl, closeServer] = await simpleEventSourceServer({
"/build/gh/test/test": responseContents,
});
console.log(serverUrl);
let responseContents = readFileSync(
`${__dirname}/fixtures/fullbuild.eventsource`,
{ encoding: "utf-8" },
);
beforeEach(async () => {
[serverUrl, closeServer] = await simpleEventSourceServer({
"/build/gh/test/test": responseContents,
});
console.log(serverUrl);
});

afterEach(() => closeServer());
test("Iterate over full output from fetch", async () => {
let i = 0;
const buildEndpointUrl = new URL(`${serverUrl}/build`);
const br = new BinderRepository("gh/test/test", buildEndpointUrl);
const messages = parseEventSource(responseContents);
for await (const item of br.fetch()) {
expect(item).toStrictEqual(messages[i]);
i += 1;
if (item.phase && item.phase === "ready") {
br.close();
}
afterEach(() => closeServer());
test("Iterate over full output from fetch", async () => {
let i = 0;
const buildEndpointUrl = new URL(`${serverUrl}/build`);
const br = new BinderRepository("gh/test/test", buildEndpointUrl);
const messages = parseEventSource(responseContents);
for await (const item of br.fetch()) {
expect(item).toStrictEqual(messages[i]);
i += 1;
if (item.phase && item.phase === "ready") {
br.close();
}
});
},
10 * 1000,
);
}
});
});

describe(
"Invalid eventsource response causes failure",
() => {
let closeServer, serverUrl;
describe("Invalid eventsource response causes failure", () => {
let closeServer, serverUrl;

beforeEach(async () => {
[serverUrl, closeServer] = await simpleEventSourceServer({
"/build/gh/test/test": "invalid",
});
beforeEach(async () => {
[serverUrl, closeServer] = await simpleEventSourceServer({
"/build/gh/test/test": "invalid",
});
});

afterEach(() => closeServer());
test("Invalid eventsource response should cause failure", async () => {
const buildEndpointUrl = new URL(`${serverUrl}/build`);
const br = new BinderRepository("gh/test/test", buildEndpointUrl);
for await (const item of br.fetch()) {
expect(item).toStrictEqual({
phase: "failed",
message: "Failed to connect to event stream\n",
});
}
});
},
10 * 1000,
);
afterEach(() => closeServer());
test("Invalid eventsource response should cause failure", async () => {
const buildEndpointUrl = new URL(`${serverUrl}/build`);
const br = new BinderRepository("gh/test/test", buildEndpointUrl);
for await (const item of br.fetch()) {
expect(item).toStrictEqual({
phase: "failed",
message: "Failed to connect to event stream\n",
});
}
});
});

test("Get full redirect URL and deal with query and encoded query (with pathType=url)", () => {
const br = new BinderRepository(
Expand All @@ -266,7 +261,7 @@ test("Get full redirect URL and deal with query and encoded query (with pathType
expect(
br
.getFullRedirectURL(
"https://hub.test-binder.org/user/something/",
new URL("https://hub.test-binder.org/user/something/"),
"token",
// url path here is already url encoded
"endpoint?a=1%2F2&b=3%3F%2F",
Expand All @@ -287,7 +282,7 @@ test("Get full redirect URL with nbgitpuller URL", () => {
expect(
br
.getFullRedirectURL(
"https://hub.test-binder.org/user/something/",
new URL("https://hub.test-binder.org/user/something/"),
"token",
// urlpath is not actually url encoded - note that / is / not %2F
"git-pull?repo=https://github.com/alperyilmaz/jupyterlab-python-intro&urlpath=lab/tree/jupyterlab-python-intro/&branch=master",
Expand Down
4 changes: 2 additions & 2 deletions js/packages/binderhub-client/tests/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import http from "node:http";
import { createServer } from "node:http";

/**
* Parse an existing stored EventSource response body into an array of JSON objects
Expand Down Expand Up @@ -29,7 +29,7 @@ export function parseEventSource(responseBody) {
*/
export async function simpleEventSourceServer(fakeResponses) {
return new Promise((resolve) => {
const server = http.createServer(async (req, res) => {
const server = createServer(async (req, res) => {
if (fakeResponses[req.url]) {
res.statusCode = 200;
res.setHeader("Content-Type", "text/event-stream");
Expand Down

0 comments on commit b2e2ed6

Please sign in to comment.