Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS: Fix some JSDoc types, and misc details in tests #1783

Merged
merged 1 commit into from
Oct 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading