Make it clearer what the expectations for isUsernamePassword are

This commit is contained in:
Michael B. Gale
2026-04-30 12:49:49 +01:00
parent 7a6ed56219
commit 549683cee5
6 changed files with 149 additions and 20 deletions
+33 -1
View File
@@ -1,6 +1,6 @@
import test from "ava";
import { makeFromSchema } from "../json/testing-util";
import { makeFromSchema, withSchemaMatrix } from "../json/testing-util";
import { setupTests } from "../testing-utils";
import * as types from "./types";
@@ -27,6 +27,38 @@ const validJFrogCredential: types.JFrogConfig = {
"identity-mapping-name": "my-mapping",
};
test("hasUsername", (t) => {
// Reject the case where `username` is missing.
t.false(types.hasUsername({}));
// Test all cases where `username` is present.
withSchemaMatrix(
t,
types.usernameSchema,
{ excludeAbsent: true },
(value) => {
t.true(types.hasUsername(value));
},
);
});
test("hasUsernameAndPassword", (t) => {
// Reject cases where `username` or `password` are missing.
t.false(types.hasUsernameAndPassword({}));
t.false(types.hasUsernameAndPassword({ username: "foo" }));
t.false(types.hasUsernameAndPassword({ password: "foo" }));
// Test all cases where both `username` and `password` are present.
withSchemaMatrix(
t,
types.usernamePasswordSchema,
{ excludeAbsent: true },
(value) => {
t.true(types.hasUsernameAndPassword(value));
},
);
});
test("credentialToStr - pretty-prints valid username+password configurations", (t) => {
const secret = "password123";
const credential: types.Credential = {
+20 -8
View File
@@ -18,10 +18,11 @@ export const usernameSchema = {
/** Usernames may be present for both authentication with tokens or passwords. */
export type Username = json.FromSchema<typeof usernameSchema>;
/** Decides whether `config` has a username. */
export function hasUsername(
config: UnvalidatedObject<unknown>,
): config is Username {
/**
* Narrows `config` to `Username` if `config` has a `username` property.
* Not used for validation. Assumes that `config` is already a validated `AuthConfig`.
*/
export function hasUsername(config: AuthConfig): config is Username {
return "username" in config;
}
@@ -38,11 +39,14 @@ export const usernamePasswordSchema = {
*/
export type UsernamePassword = json.FromSchema<typeof usernamePasswordSchema>;
/** Decides whether `config` is based on a username and password. */
export function isUsernamePassword(
/**
* Narrows `config` to `UsernamePassword` if it has a `username` and `password` property.
* Not used for validation. Assumes that `config` is already a validated `AuthConfig`.
*/
export function hasUsernameAndPassword(
config: AuthConfig,
): config is UsernamePassword {
return json.validateSchema(usernamePasswordSchema, config);
return hasUsername(config) && "password" in config;
}
/** A schema for credential objects for token-based authentication. */
@@ -58,6 +62,14 @@ export const tokenSchema = {
*/
export type Token = json.FromSchema<typeof tokenSchema>;
/**
* Narrows `config` to `Token` if it has a `token` property.
* Not used for validation. Assumes that `config` is already a validated `AuthConfig`.
*/
export function hasToken(config: AuthConfig): config is Token {
return "token" in config;
}
/** Decides whether `config` is token-based. */
export function isToken(
config: UnvalidatedObject<AuthConfig>,
@@ -205,7 +217,7 @@ export function credentialToStr(credential: Credential): string {
isDefined(credential.password) ? "***" : undefined,
);
}
if (isToken(credential)) {
if (hasToken(credential)) {
appendIfDefined("Token", isDefined(credential.token) ? "***" : undefined);
}