Skip to content
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
212 changes: 212 additions & 0 deletions __tests__/schema/opportunity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3720,6 +3720,214 @@ describe('mutation editOpportunity', () => {
'Only opportunities in draft state can be edited',
);
});

it('should edit opportunity with organization data', async () => {
loggedUser = '1';

const MUTATION_WITH_ORG = /* GraphQL */ `
mutation EditOpportunityWithOrg(
$id: ID!
$payload: OpportunityEditInput!
) {
editOpportunity(id: $id, payload: $payload) {
id
organization {
id
website
description
perks
founded
location
category
size
stage
}
}
}
`;

const res = await client.mutate(MUTATION_WITH_ORG, {
variables: {
id: opportunitiesFixture[0].id,
payload: {
organization: {
website: 'https://updated.dev',
description: 'Updated description',
perks: ['Remote work', 'Flexible hours'],
founded: 2021,
location: 'Berlin, Germany',
category: 'Technology',
size: CompanySize.COMPANY_SIZE_51_200,
stage: CompanyStage.SERIES_B,
},
},
},
});

expect(res.errors).toBeFalsy();
expect(res.data.editOpportunity.organization).toMatchObject({
website: 'https://updated.dev',
description: 'Updated description',
perks: ['Remote work', 'Flexible hours'],
founded: 2021,
location: 'Berlin, Germany',
category: 'Technology',
size: CompanySize.COMPANY_SIZE_51_200,
stage: CompanyStage.SERIES_B,
});

// Verify the organization was updated in database
const organization = await con
.getRepository(Organization)
.findOneBy({ id: organizationsFixture[0].id });

expect(organization).toMatchObject({
website: 'https://updated.dev',
description: 'Updated description',
perks: ['Remote work', 'Flexible hours'],
founded: 2021,
location: 'Berlin, Germany',
category: 'Technology',
size: CompanySize.COMPANY_SIZE_51_200,
stage: CompanyStage.SERIES_B,
});
});
});

describe('mutation clearOrganizationImage', () => {
beforeEach(async () => {
await con.getRepository(OpportunityJob).update(
{
id: opportunitiesFixture[0].id,
},
{
state: OpportunityState.DRAFT,
},
);
});

const MUTATION = /* GraphQL */ `
mutation ClearOrganizationImage($id: ID!) {
clearOrganizationImage(id: $id) {
_
}
}
`;

it('should require authentication', async () => {
await testMutationErrorCode(
client,
{
mutation: MUTATION,
variables: {
id: opportunitiesFixture[0].id,
},
},
'UNAUTHENTICATED',
);
});

it('should throw error when user is not a recruiter for opportunity', async () => {
loggedUser = '2';

await testMutationErrorCode(
client,
{
mutation: MUTATION,
variables: {
id: opportunitiesFixture[0].id,
},
},
'FORBIDDEN',
'Access denied!',
);
});

it('should throw error when opportunity does not exist', async () => {
loggedUser = '1';

await testMutationErrorCode(
client,
{
mutation: MUTATION,
variables: {
id: '660e8400-e29b-41d4-a716-446655440999',
},
},
'FORBIDDEN',
);
});

it('should clear organization image', async () => {
loggedUser = '1';

// First set an image on the organization
await con
.getRepository(Organization)
.update(
{ id: organizationsFixture[0].id },
{ image: 'https://example.com/old-image.png' },
);

// Verify image is set
let organization = await con
.getRepository(Organization)
.findOneBy({ id: organizationsFixture[0].id });
expect(organization?.image).toBe('https://example.com/old-image.png');

// Clear the image
const res = await client.mutate(MUTATION, {
variables: {
id: opportunitiesFixture[0].id,
},
});

expect(res.errors).toBeFalsy();
expect(res.data.clearOrganizationImage).toEqual({ _: true });

// Verify image was cleared in database
organization = await con
.getRepository(Organization)
.findOneBy({ id: organizationsFixture[0].id });
expect(organization?.image).toBeNull();
});

it('should work with opportunity permissions not direct organization permissions', async () => {
loggedUser = '3';

// User 3 is not a recruiter for opportunity 0, but let's make them one
await saveFixtures(con, OpportunityUser, [
{
opportunityId: opportunitiesFixture[0].id,
userId: '3',
type: OpportunityUserType.Recruiter,
},
]);

// Set an image on the organization
await con
.getRepository(Organization)
.update(
{ id: organizationsFixture[0].id },
{ image: 'https://example.com/test-image.png' },
);

// Should be able to clear the image through opportunity permissions
const res = await client.mutate(MUTATION, {
variables: {
id: opportunitiesFixture[0].id,
},
});

expect(res.errors).toBeFalsy();
expect(res.data.clearOrganizationImage).toEqual({ _: true });

// Verify image was cleared
const organization = await con
.getRepository(Organization)
.findOneBy({ id: organizationsFixture[0].id });
expect(organization?.image).toBeNull();
});
});

describe('mutation recommendOpportunityScreeningQuestions', () => {
Expand Down Expand Up @@ -3955,6 +4163,10 @@ describe('mutation updateOpportunityState', () => {
'content.responsibilities',
'content.requirements',
'questions',
'organization.links.0.socialType',
'organization.links.1.socialType',
'organization.links.2.title',
'organization.links.3.socialType',
]);
},
);
Expand Down
12 changes: 12 additions & 0 deletions src/common/schema/opportunities.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { OpportunityState } from '@dailydotdev/schema';
import z from 'zod';
import { organizationLinksSchema } from './organizations';

export const opportunityMatchDescriptionSchema = z.object({
reasoning: z.string(),
Expand Down Expand Up @@ -143,6 +144,17 @@ export const opportunityEditSchema = z
)
.min(1)
.max(3),
organization: z.object({
website: z.string().max(500).nullable().optional(),
description: z.string().max(2000).nullable().optional(),
perks: z.array(z.string().max(240)).max(50).nullable().optional(),
founded: z.number().int().min(1800).max(2100).nullable().optional(),
location: z.string().max(500).nullable().optional(),
category: z.string().max(240).nullable().optional(),
size: z.number().int().nullable().optional(),
stage: z.number().int().nullable().optional(),
links: z.array(organizationLinksSchema).max(50).optional(),
}),
})
.partial();

Expand Down
2 changes: 1 addition & 1 deletion src/entity/Organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class Organization {
name: string;

@Column({ type: 'text', nullable: true })
image: string;
image: string | null;

@Column({ type: 'smallint', default: 1 })
seats: number;
Expand Down
Loading
Loading