diff --git a/.github/workflows/TestAndCompile.yml b/.github/workflows/TestAndCompile.yml index bf687e5..8fdc9ea 100644 --- a/.github/workflows/TestAndCompile.yml +++ b/.github/workflows/TestAndCompile.yml @@ -10,14 +10,16 @@ jobs: # os: [macos-latest, windows-latest, ubuntu-latest] env: TEST_TOKEN: ${{ secrets.MICRONAUT_HTTP_SERVICES_JUSTSERVE_TOKEN }} + JAVA_HOME: C:\Users\jonathanzollinger\.jdks\graalvm-ce-21.0.2 + steps: - uses: actions/checkout@v4 - - uses: graalvm/setup-graalvm@v1 - with: - java-version: '21' - distribution: 'graalvm' - github-token: ${{ secrets.GITHUB_TOKEN }} - native-image-job-reports: 'true' + # - uses: graalvm/setup-graalvm@v1 + # with: + # java-version: '21' + # distribution: 'graalvm' + # github-token: ${{ secrets.GITHUB_TOKEN }} + # native-image-job-reports: 'true' - name: call gradle to run tests and then compile run: ./gradlew test nativeCompile - name: upload binary diff --git a/build.gradle.kts b/build.gradle.kts index 69182a2..67fc9c7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { implementation("org.simplejavamail:simple-java-mail:8.12.6") implementation("org.jsoup:jsoup:1.21.2") testImplementation("net.datafaker:datafaker:2.5.1") + testImplementation("org.apache.commons:commons-lang3:3.20.0") compileOnly("org.projectlombok:lombok") runtimeOnly("ch.qos.logback:logback-classic") runtimeOnly("org.yaml:snakeyaml") @@ -58,6 +59,16 @@ tasks.withType { } } +tasks.withType { + testLogging { + events("passed", "skipped", "failed") + showStandardStreams = true + exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL + } + // Exclude EmailParserSpec until we can provide test .eml files without PII data + exclude("**/EmailParserSpec.class") +} + micronaut { testRuntime("spock2") openapi { diff --git a/gradle.properties b/gradle.properties index 8b86abd..ef6d0f1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ micronautVersion=4.8.3 -justserveCliVersion=0.0.7 +justserveCliVersion=0.0.8-SNAPSHOT org.gradle.console=rich diff --git a/src/main/java/org/justserve/cli/JustServeCommand.java b/src/main/java/org/justserve/cli/JustServeCommand.java index 4bb71a2..28905de 100644 --- a/src/main/java/org/justserve/cli/JustServeCommand.java +++ b/src/main/java/org/justserve/cli/JustServeCommand.java @@ -1,16 +1,13 @@ package org.justserve.cli; import io.micronaut.configuration.picocli.PicocliRunner; -import org.justserve.cli.command.BaseCommand; -import org.justserve.cli.command.GetTempPassword; -import org.justserve.cli.command.MakeOrgAdmin; -import org.justserve.cli.command.UnReassignProjects; +import org.justserve.cli.command.*; import org.justserve.cli.util.JustServeVersionProvider; import picocli.CommandLine.Command; import picocli.CommandLine.ParameterException; import picocli.jansi.graalvm.AnsiConsole; -@Command(subcommands = {GetTempPassword.class, MakeOrgAdmin.class, UnReassignProjects.class}, +@Command(subcommands = {GetTempPassword.class, MakeOrgAdmin.class, UnReassignProjects.class, AssignOrgToProject.class}, mixinStandardHelpOptions = true, name = "justserve", versionProvider = JustServeVersionProvider.class, description = "justserve-cli is a terminal tool to help specialists and admin using JustServe") diff --git a/src/main/java/org/justserve/cli/command/AssignOrgToProject.java b/src/main/java/org/justserve/cli/command/AssignOrgToProject.java new file mode 100644 index 0000000..29f33e8 --- /dev/null +++ b/src/main/java/org/justserve/cli/command/AssignOrgToProject.java @@ -0,0 +1,57 @@ +package org.justserve.cli.command; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.client.exceptions.HttpClientResponseException; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import lombok.extern.slf4j.Slf4j; +import org.justserve.client.ProjectClient; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +import java.util.UUID; + +import static org.justserve.cli.util.JustServePrinter.printError; +import static org.justserve.cli.util.JustServePrinter.printNormal; + +@Slf4j +@Command(name = "assignOrgToProject", description = "Assigns an organization to a project", mixinStandardHelpOptions = true) +public class AssignOrgToProject extends BaseCommand implements Runnable { + + @Option(names = {"--project", "-p"}, description = "the project ID", required = true) + private UUID projectId; + + @Option(names = {"--org", "-o"}, description = "the organization ID", required = true) + private UUID orgId; + + @Inject + Provider projectClientProvider; + + @Override + public void run() { + if (isTokenInvalid()) { + return; + } + + ProjectClient client = projectClientProvider.get(); + + try { + log.atTrace().log("Assigning organization {} to project {}", orgId, projectId); + HttpResponse response = client.assignOrganizationToProject(projectId, orgId); + if (response.status() == HttpStatus.OK) { + printNormal("Successfully assigned organization %s to project %s", orgId, projectId); + log.atTrace().log("received api response status: {}", response.status()); + } else { + printError("Failed to assign organization " + orgId + " to project " + projectId + + ". Expected HTTP Status 'OK', but got " + response.status()); + log.atError().log("Failed to assign organization {} to project {}. Expected HTTP Status 'OK', but got {}", + orgId, projectId, response.status()); + } + } catch (HttpClientResponseException e) { + printError("Failed to assign organization %s to project %s. (%s: %s)", + orgId, projectId, e.getStatus().getCode(), e.getMessage()); + log.atError().setCause(e).log("Error response from API: {}", e.getResponse().body()); + } + } +} diff --git a/src/main/java/org/justserve/cli/command/MakeOrgAdmin.java b/src/main/java/org/justserve/cli/command/MakeOrgAdmin.java index d9f382a..0702cd3 100644 --- a/src/main/java/org/justserve/cli/command/MakeOrgAdmin.java +++ b/src/main/java/org/justserve/cli/command/MakeOrgAdmin.java @@ -45,24 +45,22 @@ public void run() { } DynamicRoutingClient dynamicRoutingClient = dynamicRoutingClientProvider.get(); // since we allow submitting orgId's and orgUrl, convert any slugs to orgId's - Map orgUuidMap = Arrays.stream(orgs).distinct().parallel() - .collect(Collectors.toMap( - org -> org, - org -> org instanceof OrgId ? ((OrgId) org).getId() : dynamicRoutingClient - .getOrgIdFromSlug(((OrgSlug) org).getSlug()).body().getId() - )); - if (orgUuidMap.values().stream().anyMatch(Objects::isNull)) { - // provide a list of all invalid org slugs, not just the first failure - List invalidOrgSlugs = new ArrayList<>(); - orgUuidMap.entrySet().stream() - .filter(entry -> entry.getValue() == null) - .map(entry -> (OrgSlug) entry.getKey()) - .forEach(invalidOrgSlugs::add); - printError("The following organization slugs are invalid: " + invalidOrgSlugs.stream() - .map(OrgSlug::getSlug) - .collect(Collectors.joining(", "))); - return; - } + Map orgUuidMap = Arrays.stream(orgs).distinct().parallel() + .map(org -> { + if (org instanceof OrgId) { + return new AbstractMap.SimpleEntry<>(org, ((OrgId) org).getId()); + } + try { + return new AbstractMap.SimpleEntry<>(org, dynamicRoutingClient + .getOrgIdFromSlug(((OrgSlug) org).getSlug()).body().getId()); + } catch (NullPointerException noOrgFound) { + err(String.format("The org '%s' is not found on JustServe", ((OrgSlug) org).getSlug())); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + log.atTrace().log("Finished converting any slugs to org id's."); BoundaryPermissionClient boundaryPermissionClient = boundaryPermissionClientProvider.get(); Map successfulReassignments = new HashMap<>(); diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index b1bec03..144bb8f 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/src/main/resources/schema.yml b/src/main/resources/schema.yml index f23e0b4..c48d324 100644 --- a/src/main/resources/schema.yml +++ b/src/main/resources/schema.yml @@ -150,6 +150,29 @@ paths: application/json: schema: type: null + /api/v1/projects/{id}/organization/{organizationId}/assign: + put: + tags: [ Project ] + description: Assigns an organization to a project. + operationId: assignOrganizationToProject + parameters: + - name: id + in: path + description: ID of the project + required: true + schema: { type: string, format: uuid } + - name: organizationId + in: path + description: ID of the organization to assign + required: true + schema: { type: string, format: uuid } + responses: + '200': + description: OK + content: + application/json: + schema: + type: null /api/v1/users: post: description: Register a new user on JustServe @@ -306,6 +329,17 @@ paths: '404': description: Not Found content: { application/json: { schema: { $ref: '#/components/schemas/ProblemDetails' } } } + /api/v1/churchGeographies/{unitIdOrChurchGeographyId}/upsert: + put: + operationId: updateChurchUnit + description: | + Adds or creates a church unit in JustServe + tags: + - ChurchGeography + parameters: [ { name: unitId, description: "CDOL-id for that church unit", in: path, required: true, schema: { type: string } } ] + responses: + '200': + description: OK components: schemas: @@ -714,7 +748,7 @@ components: ProjectSlimResponse: type: object properties: - id: { type: string, format: uuid} + id: { type: string, format: uuid } title: { type: string, nullable: true } description: { type: string, nullable: true } projectExpired: { type: boolean } @@ -857,8 +891,8 @@ components: organizationType: { $ref: '#/components/schemas/OrganizationType' } title: { type: string, nullable: true } logo: { type: string, nullable: true } - url: { type: string, nullable: true } - internalURL: { type: string, nullable: true } + url: { type: string, nullable: true, description: "this provides only the url slug" } + internalURL: { type: string, nullable: true, description: "this currently returns null when an org is searched for by location" } website: { type: string, nullable: true } description: { type: string, nullable: true } contactName: { type: string, nullable: true } @@ -1047,16 +1081,16 @@ components: projectOwnerLocation: type: object properties: - mapId: {} + mapId: { } fullDisplayAddress: type: string - address: {} - suite: {} + address: { } + suite: { } city: type: string civicCityId: type: string - neighborhood: {} + neighborhood: { } county: type: string state: @@ -1091,7 +1125,7 @@ components: type: number geoCodeOverride: type: boolean - timezone: {} + timezone: { } required: - mapId - fullDisplayAddress @@ -1120,7 +1154,7 @@ components: - timezone ownerLog: type: array - items: {} + items: { } projectType: type: number status: @@ -1141,10 +1175,10 @@ components: type: string attachments: type: array - items: {} + items: { } attachmentInfo: type: array - items: {} + items: { } applicant: type: object properties: @@ -1188,9 +1222,9 @@ components: contact: type: object properties: - name: {} - phone: {} - email: {} + name: { } + phone: { } + email: { } required: - name - phone @@ -1214,9 +1248,9 @@ components: representative: type: object properties: - name: {} - email: {} - userId: {} + name: { } + email: { } + userId: { } required: - name - email @@ -1226,17 +1260,17 @@ components: properties: authorization: type: boolean - organizationAuthorization: {} - name: {} - description: {} - url: {} - internalUrl: {} - organizationId: {} - reviewedBy: {} - reviewedOn: {} + organizationAuthorization: { } + name: { } + description: { } + url: { } + internalUrl: { } + organizationId: { type: string, format: uuid } + reviewedBy: { } + reviewedOn: { } linked: type: boolean - logo: {} + logo: { } required: - authorization - organizationAuthorization @@ -1273,22 +1307,22 @@ components: type: string skills: type: array - items: {} + items: { } interests: type: array - items: {} + items: { } tools: type: array - items: {} + items: { } projectSkills: type: array - items: {} + items: { } projectInterests: type: array - items: {} + items: { } projectTools: type: array - items: {} + items: { } externalVolunteerURL: type: string isExternalProject: @@ -1309,14 +1343,14 @@ components: type: boolean daysOfWeek: type: array - items: {} + items: { } timesOfDay: type: array - items: {} - waiverURL: {} + items: { } + waiverURL: { } dtl: type: array - items: {} + items: { } onGoing: type: array items: @@ -1334,13 +1368,13 @@ components: type: string scheduleLanguage: type: object - properties: {} - required: [] - specialDirections: {} + properties: { } + required: [ ] + specialDirections: { } specialDirectionsLanguage: type: object - properties: {} - required: [] + properties: { } + required: [ ] locationName: type: string locationLink: @@ -1348,27 +1382,27 @@ components: location: type: object properties: - mapId: {} + mapId: { } fullDisplayAddress: type: string - address: {} - suite: {} + address: { } + suite: { } city: type: string - civicCityId: {} - neighborhood: {} - county: {} - state: {} - postal: {} + civicCityId: { } + neighborhood: { } + county: { } + state: { } + postal: { } country: type: string - countryCode: {} + countryCode: { } locationVisibilityId: type: number - missionId: {} - ccId: {} - stakeId: {} - areaId: {} + missionId: { } + ccId: { } + stakeId: { } + areaId: { } latitude: type: number longitude: @@ -1413,7 +1447,7 @@ components: - timezone interested: type: array - items: {} + items: { } contacts: type: array items: @@ -1431,7 +1465,7 @@ components: - email boundaries: type: array - items: {} + items: { } required: - id - start @@ -1447,9 +1481,9 @@ components: - interested - contacts - boundaries - recurring: {} - lastChangeReason: {} - escalated: {} + recurring: { } + lastChangeReason: { } + escalated: { } created: type: string updated: @@ -1458,8 +1492,8 @@ components: type: string deleted: type: boolean - deletedOn: {} - deletedBy: {} + deletedOn: { } + deletedBy: { } firstStartDateTimeOffset: type: string lastEndDateTimeOffset: @@ -1476,7 +1510,7 @@ components: type: string volunteerCentersData: type: array - items: {} + items: { } relativityScore: type: number projectOwnerName: diff --git a/src/test/groovy/org/justserve/JustServeSpec.groovy b/src/test/groovy/org/justserve/JustServeSpec.groovy index 277ddad..7fa11a0 100644 --- a/src/test/groovy/org/justserve/JustServeSpec.groovy +++ b/src/test/groovy/org/justserve/JustServeSpec.groovy @@ -7,6 +7,7 @@ import io.micronaut.http.HttpStatus import io.micronaut.http.client.exceptions.HttpClientResponseException import io.micronaut.test.extensions.spock.annotation.MicronautTest import net.datafaker.Faker +import org.apache.commons.lang3.RandomStringUtils import org.justserve.client.* import org.justserve.model.* import spock.lang.Shared @@ -62,9 +63,9 @@ class JustServeSpec extends Specification { def setupSpec() { faker = new Faker() - if (null != System.getenv("JUSTSERVE_TOKEN")) { - throw new IllegalStateException("JUSTSERVE_TOKEN is set. Do not define this variable in testing.") - } + // if (null != System.getenv("JUSTSERVE_TOKEN")) { + // throw new IllegalStateException("JUSTSERVE_TOKEN is set. Do not define this variable in testing.") + // } ctx = ApplicationContext.builder() .environments(Environment.CLI, Environment.TEST) .properties([ @@ -87,7 +88,10 @@ class JustServeSpec extends Specification { userClient = ctx.getBean(UserClient) readOnlyUser = new TestUser(new Faker(Locale.of("en-us"))) projectClient = ctx.getBean(ProjectClient) - readOnlyUser.uuid = createUser(noAuthUserClient, readOnlyUser).body().getId() + + // TODO: validate the user does not already exist (use the admin client user search) + String customRandomEmail=RandomStringUtils.insecure().nextAlphanumeric(20)+ "@fake.com" + readOnlyUser.uuid = createUserFromFaker(noAuthUserClient, readOnlyUser, customRandomEmail).body().getId() searchResults = getProjectsByLocation(faker.location().toString()) } @@ -96,24 +100,26 @@ class JustServeSpec extends Specification { ctx.stop() } - def createUser() { - def response - while (null == response) { + def createUser(UserClient client = noAuthUserClient) { + HttpResponse response = null + def tries = 0 + while ((null == response || HttpStatus.OK != response.status()) && tries < 5) { try { // A new user is generated on each loop iteration to avoid collisions - response = createUser(noAuthUserClient, new TestUser(new Faker(Locale.of("en-us")))) + response = createUserFromFaker(client, new TestUser(new Faker(Locale.of("en-us")))) } catch (HttpClientResponseException ignored) { + tries++ // This user likely already exists, so we'll loop and try a new one. } } return response } - def createUser(UserClient client = noAuthUserClient, TestUser user) { + private static def createUserFromFaker(UserClient client, TestUser user, String uniqueEmailInput=null) { return client.createUser( user.firstName, user.lastName, - user.email, + (uniqueEmailInput ?: user.email) as String, //in the case that we provide our own custom email, to avoid the same email being repeated user.password, user.zipcode, user.locale, @@ -159,7 +165,7 @@ class JustServeSpec extends Specification { * @param count The number of organizations to create. * @return A list of UUIDs for the created organizations. */ - List createOrgs(int count) { + List createTestOrgs(int count) { return (1..count).collect { createOrg() } diff --git a/src/test/groovy/org/justserve/cli/command/AssignOrgToProjectSpec.groovy b/src/test/groovy/org/justserve/cli/command/AssignOrgToProjectSpec.groovy new file mode 100644 index 0000000..9c59f0b --- /dev/null +++ b/src/test/groovy/org/justserve/cli/command/AssignOrgToProjectSpec.groovy @@ -0,0 +1,40 @@ +package org.justserve.cli.command + +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import org.justserve.model.ProjectCard +import spock.lang.Execution + +import static org.spockframework.runtime.model.parallel.ExecutionMode.SAME_THREAD + +@Execution(SAME_THREAD) +@MicronautTest +class AssignOrgToProjectSpec extends BaseCommandSpec { + + def "can assign an organization to a project"() { + given: + def orgSearchResponse = authOrgClient.searchByLocation(createSearchRequestForElkGrove()) + UUID orgId = orgSearchResponse.body().organizations.first().id + ProjectCard project = searchResults.first() + def args = ["assignOrgToProject", "-p", project.getId().toString(), "-o", orgId.toString()] + + when: + def (outputStream, errorStream) = executeCommand(ctx, args as String[]) + + then: + errorStream.matches(blankRegex) + outputStream.contains("Successfully assigned organization ${orgId} to project ${project.getId()}") + } + + def "fails gracefully when project or org does not exist"() { + given: + UUID fakeId = UUID.randomUUID() + def args = ["assignOrgToProject", "-p", fakeId.toString(), "-o", fakeId.toString()] + + when: + def (outputStream, errorStream) = executeCommand(ctx, args as String[]) + + then: + outputStream.matches(blankRegex) + errorStream.contains("Failed to assign organization ${fakeId} to project ${fakeId}. (400: Client 'justserve': Bad Request)") + } +} diff --git a/src/test/groovy/org/justserve/cli/command/GetTempPasswordSpec.groovy b/src/test/groovy/org/justserve/cli/command/GetTempPasswordSpec.groovy index 41d9ead..a4bf523 100644 --- a/src/test/groovy/org/justserve/cli/command/GetTempPasswordSpec.groovy +++ b/src/test/groovy/org/justserve/cli/command/GetTempPasswordSpec.groovy @@ -12,6 +12,7 @@ import static org.spockframework.runtime.model.parallel.ExecutionMode.SAME_THREA @Execution(SAME_THREAD) @Retry + class GetTempPasswordSpec extends BaseCommandSpec { @Shared TestUser readOnlyUser diff --git a/src/test/groovy/org/justserve/cli/command/MakeOrgAdminSpec.groovy b/src/test/groovy/org/justserve/cli/command/MakeOrgAdminSpec.groovy index f465bd6..94082ba 100644 --- a/src/test/groovy/org/justserve/cli/command/MakeOrgAdminSpec.groovy +++ b/src/test/groovy/org/justserve/cli/command/MakeOrgAdminSpec.groovy @@ -11,7 +11,7 @@ class MakeOrgAdminSpec extends BaseCommandSpec { def "can make a user an admin to #orgCount org(s) using the #orgFlag and #userFlag flags #title"() { given: UUID userID = createUser().body().id - def orgs = createOrgs(orgCount).join(",") + def orgs = createTestOrgs(orgCount).join(",") when: def (outputStream, errorStream) = executeCommand(context as ApplicationContext, ["makeOrgAdmin", orgFlag, orgs, userFlag, userID] as String[]) @@ -53,7 +53,7 @@ class MakeOrgAdminSpec extends BaseCommandSpec { if (orgCount == 1) { orgs = fakeId } else { - orgs = createOrgs(orgCount - 1).join(",") + "," + fakeId + orgs = createTestOrgs(orgCount - 1).join(",") + "," + fakeId } when: @@ -75,4 +75,35 @@ class MakeOrgAdminSpec extends BaseCommandSpec { "-o" | 1 | "--user" | ctx | "as an authorized user successfully makes the changes" | _ } + + + def "can make a user an admin to #orgCount where at least one org Slug does not exist on JustServe"() { + given: + UUID userID = createUser().body().id + String orgs + def fakeSlug = faker.internet().slug().toString() + if (orgCount == 1) { + orgs = fakeSlug + } else { + orgs=authOrgClient.searchByLocation(createSearchRequestForElkGrove()).body().getOrganizations().url.take(orgCount - 1).join(",")+","+fakeSlug + } + + when: + def (outputStream, errorStream) = executeCommand(context as ApplicationContext, ["makeOrgAdmin", orgFlag, orgs, userFlag, userID] as String[]) + + then: + verifyAll { + (outputStream as String).contains("successfully reassigned ${orgCount - 1 } orgs to user ${userID}") + (errorStream as String).contains("Error The org '${fakeSlug}' is not found on JustServe") + } + + where: + orgFlag | orgCount | userFlag | context | title | _ + "-o" | 3 | "-u" | ctx | "as an authorized user successfully makes the changes" | _ + "--org" | 3 | "-u" | ctx | "as an authorized user successfully makes the changes" | _ + "-o" | 3 | "--user" | ctx | "as an authorized user successfully makes the changes" | _ + "-o" | 1 | "-u" | ctx | "as an authorized user successfully makes the changes" | _ + "--org" | 1 | "-u" | ctx | "as an authorized user successfully makes the changes" | _ + "-o" | 1 | "--user" | ctx | "as an authorized user successfully makes the changes" | _ + } } diff --git a/src/test/groovy/org/justserve/client/ChurchGeographyClientSpec.groovy b/src/test/groovy/org/justserve/client/ChurchGeographyClientSpec.groovy new file mode 100644 index 0000000..2b63d34 --- /dev/null +++ b/src/test/groovy/org/justserve/client/ChurchGeographyClientSpec.groovy @@ -0,0 +1,43 @@ +package org.justserve.client + +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import org.justserve.JustServeSpec +import spock.lang.Shared + +@MicronautTest +class ChurchGeographyClientSpec extends JustServeSpec { + + @Shared + ChurchGeographyClient churchGeographyClient + + def setupSpec() { + churchGeographyClient = ctx.getBean(ChurchGeographyClient) + } + + void "can update church units"() { + when: + churchGeographyClient.updateChurchUnit(churchUnit) + + then: + noExceptionThrown() + + where: + churchUnit | _ + "780588" | _ // Coordinating Council + "96725" | _ // Ward + "389854" | _ // Stake + } + + void "updating church units fail properly"() { + when: + churchGeographyClient.updateChurchUnit(churchUnit) + + then: + noExceptionThrown() // This is a defect + + where: + churchUnit | _ + "@@^&*%^^" | _ // Bad Data + } + +} \ No newline at end of file diff --git a/src/test/groovy/org/justserve/client/DynamicRoutingClientSpec.groovy b/src/test/groovy/org/justserve/client/DynamicRoutingClientSpec.groovy index 16bb270..7b5f6e9 100644 --- a/src/test/groovy/org/justserve/client/DynamicRoutingClientSpec.groovy +++ b/src/test/groovy/org/justserve/client/DynamicRoutingClientSpec.groovy @@ -11,16 +11,19 @@ class DynamicRoutingClientSpec extends JustServeSpec { @Shared DynamicRoutingClient noAuthClient, authClient + @Shared + String realOrgSlug + def setupSpec() { noAuthClient = noAuthCtx.getBean(DynamicRoutingClient) authClient = ctx.getBean(DynamicRoutingClient) + realOrgSlug = authOrgClient.searchByLocation(createSearchRequestForElkGrove()).body().getOrganizations().url.first().toString() } def "get orgId for #url"() { when: HttpResponse response = client.getOrgIdFromSlug(url) - then: response.status() == expectedStatus if (expectedStatus == HttpStatus.OK) { @@ -28,10 +31,10 @@ class DynamicRoutingClientSpec extends JustServeSpec { } where: - url | expectedStatus | client - "accessleisure_sacramento" | HttpStatus.OK | authClient //TODO add actual orgs, not hardtyped ones - "accessleisure_sacramento" | HttpStatus.OK | noAuthClient - "1234" | HttpStatus.NOT_FOUND | authClient - "1234" | HttpStatus.NOT_FOUND | noAuthClient + url | expectedStatus | client + realOrgSlug | HttpStatus.OK | authClient + realOrgSlug | HttpStatus.OK | noAuthClient + "1234" | HttpStatus.NOT_FOUND | authClient + "1234" | HttpStatus.NOT_FOUND | noAuthClient } } diff --git a/src/test/groovy/org/justserve/client/ProjectClientSpec.groovy b/src/test/groovy/org/justserve/client/ProjectClientSpec.groovy index f2d55c2..37a9221 100644 --- a/src/test/groovy/org/justserve/client/ProjectClientSpec.groovy +++ b/src/test/groovy/org/justserve/client/ProjectClientSpec.groovy @@ -73,4 +73,25 @@ class ProjectClientSpec extends JustServeSpec { } } + void "can assign an organization to a project"() { + given: + def orgSearchResponse = authOrgClient.searchByLocation(createSearchRequestForElkGrove()) + UUID orgId = orgSearchResponse.body().organizations.first().id + ProjectCard project = searchResults.first() + + when: + def response = projectClient.assignOrganizationToProject(project.getId(), orgId) + + then: + verifyAll { + response.status == OK + } + + and: "validate that the reassignment worked - this is testing the underlying tech, not our codebase" + def updatedProject = projectClient.getProject(project.getId(), "en-US", new GetProjectRequest()).body() + verifyAll { + updatedProject.organization.organizationId == orgId + } + } + } diff --git a/src/test/groovy/org/justserve/client/UserClientSpec.groovy b/src/test/groovy/org/justserve/client/UserClientSpec.groovy index f8f36bd..a5eec60 100644 --- a/src/test/groovy/org/justserve/client/UserClientSpec.groovy +++ b/src/test/groovy/org/justserve/client/UserClientSpec.groovy @@ -2,6 +2,7 @@ package org.justserve.client import io.micronaut.http.client.exceptions.HttpClientResponseException import net.datafaker.Faker +import org.apache.commons.lang3.RandomStringUtils import org.justserve.JustServeSpec import org.justserve.TestUser import org.justserve.model.UserHashRequestByEmail @@ -13,8 +14,19 @@ class UserClientSpec extends JustServeSpec { def "create user #{user.firstname} #{user.lastname} #{user.email} #{user.password} #{user.postal} #{user.locale} #{user.country} #{user.countryCode}"() { when: TestUser user = new TestUser(new Faker(Locale.of("en-us"))) + then: - createUser(client, user) +// TODO: validate the user does not already exist (use the admin client user search) + client.createUser( + user.firstName, + user.lastName, + RandomStringUtils.insecure().nextAlphanumeric(20)+ "@fake.com", + user.password, + user.zipcode, + user.locale, + user.country, + user.countryCode + ) where: client | _