<commit_before>/** @module test/graphql.expenses.test
 *
 * This tests all the GraphQL API methods that interact with
 * expenses. */

/* Test libraries */
import sinon from 'sinon';
import { expect } from 'chai';

/* Test utilities */
import * as utils from './utils';
import * as store from './features/support/stores';

/* Support code */
import models from '../server/models';
import emailLib from '../server/lib/email';
import * as libtransactions from '../server/lib/transactions';
import { getFxRate } from '../server/lib/currency';

import paypalAdaptive from '../server/paymentProviders/paypal/adaptiveGateway';

/* Queries used throughout these tests */
const allExpensesQuery = `
  query allExpenses($CollectiveId: Int!, $category: String, $fromCollectiveSlug: String, $limit: Int, $includeHostedCollectives: Boolean) {
    allExpenses(CollectiveId: $CollectiveId, category: $category, fromCollectiveSlug: $fromCollectiveSlug, limit: $limit, includeHostedCollectives: $includeHostedCollectives) {
      id
      description
      amount
      category
      user { id email paypalEmail collective { id slug } }
      collective { id slug } } }`;

const expensesQuery = `
  query expenses($CollectiveId: Int, $CollectiveSlug: String, $category: String, $FromCollectiveId: Int, $FromCollectiveSlug: String, $status: ExpenseStatus, $offset: Int, $limit: Int, $orderBy: OrderByType) {
    expenses(CollectiveId: $CollectiveId, CollectiveSlug: $CollectiveSlug, category: $category, FromCollectiveId: $FromCollectiveId, FromCollectiveSlug: $FromCollectiveSlug, status: $status, offset: $offset, limit: $limit, orderBy: $orderBy) {
      expenses {
        id
        description
        amount
        category
        user { id email paypalEmail collective { id slug } }
        collective { id slug }
      }
    }
  }
`;

const createExpenseQuery = `
  mutation createExpense($expense: ExpenseInputType!) {
    createExpense(expense: $expense) {
      id
      status
      user { id name collective { id name slug } } } }`;

const approveExpenseQuery = `
  mutation approveExpense($id: Int!) {
    approveExpense(id: $id) { id status } }`;

const deleteExpenseQuery = `
  mutation deleteExpense($id: Int!) { deleteExpense(id: $id) { id } }`;

const payExpenseQuery = `
  mutation payExpense($id: Int!, $paymentProcessorFeeInCollectiveCurrency: Int, $hostFeeInCollectiveCurrency: Int, $platformFeeInCollectiveCurrency: Int) {
    payExpense(id: $id, paymentProcessorFeeInCollectiveCurrency: $paymentProcessorFeeInCollectiveCurrency, hostFeeInCollectiveCurrency: $hostFeeInCollectiveCurrency, platformFeeInCollectiveCurrency: $platformFeeInCollectiveCurrency) { id status } }`;

describe('GraphQL Expenses API', () => {
  beforeEach(utils.resetTestDB);

  beforeEach(utils.resetCaches);

  describe('#allExpenses', () => {
    it('fails if collective not found', async () => {
      // Given we have no collective with id 999999
      // When we try to retrieve expenses from an invalid collective
      const result = await utils.graphqlQuery(allExpensesQuery, {
        CollectiveId: 999999,
      });
      // Then we should see an error
      expect(result.errors).to.exist;
      // And then the error message should be appropriate
      expect(result.errors[0].message).to.equal('Collective not found');
    }); /* End of "fails if collective not found" */

    it('gets no expenses if collective has no expenses', async () => {
      // Given that we have a collective with no expenses
      const { collective } = await store.newCollectiveWithHost('railsgirlsatl', 'USD', 'USD', 10);
      // When we retrieve all its expenses
      const result = await utils.graphqlQuery(allExpensesQuery, {
        CollectiveId: collective.id,
      });
      result.errors && console.log(result.errors);
      // Then there should be no errors
      expect(result.errors).to.not.exist;
      // And then it should retrieve no expenses
      expect(result.data.allExpenses).to.have.length(0);
    }); /* End of "gets no expenses if collective has no expenses" */

    it('gets the latest expenses from one collective', async () => {
      // Given that we have a collective
      const { hostAdmin, collective } = await store.newCollectiveWithHost('railsgirlsatl', 'USD', 'USD', 10);
      // And given the above collective has some expenses
      const data = {
        currency: 'USD',
        payoutMethod: 'manual',
        collective: { id: collective.id },
      };
      await store.createExpense(hostAdmin, {
        amount: 1000,
        description: 'Pizza',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 2000,
        description: 'Beer',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 3000,
        description: 'Banner',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 4000,
        description: 'Stickers',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 5000,
        description: 'T-shirts',
        ...data,
      });
      // When we retrieve all its expenses
      const result = await utils.graphqlQuery(allExpensesQuery, {
        CollectiveId: collective.id,
        limit: 5,
      });
      result.errors && console.log(result.errors);
      expect(result.errors).to.not.exist;
      // Then it should retrieve the right amount of expenses
      const expenses = result.data.allExpenses;
      expect(expenses).to.have.length(5);
      // And then the expenses retrieved should come from the same
      // collective
      expect(expenses.map(e => e.collective.slug)).to.deep.equal([
        'railsgirlsatl',
        'railsgirlsatl',
        'railsgirlsatl',
        'railsgirlsatl',
        'railsgirlsatl',
      ]);
    }); /* End of "gets the latest expenses from one collective" */

    it('gets the latest expenses from all the hosted collectives', async () => {
      // Given that we have two collectives within a host
      const { hostAdmin, hostCollective, collective } = await store.newCollectiveWithHost('apex', 'USD', 'USD', 10);
      const anotherCollective = (await store.newCollectiveInHost('brusselstogether', 'USD', hostCollective)).collective;
      // And given that the first collective created above have two expenses
      const data = {
        currency: 'USD',
        payoutMethod: 'manual',
        collective: { id: collective.id },
      };
      await store.createExpense(hostAdmin, {
        amount: 1000,
        description: 'Pizza',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 2000,
        description: 'Beer',
        ...data,
      });
      // And given that the second collective created above also have
      // two expenses
      data.collective = { id: anotherCollective.id };
      await store.createExpense(hostAdmin, {
        amount: 3000,
        description: 'Banner',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 4000,
        description: 'Stickers',
        ...data,
      });
      // When we retrieve all the expenses of the host
      const result = await utils.graphqlQuery(allExpensesQuery, {
        CollectiveId: hostCollective.id,
        limit: 5,
        includeHostedCollectives: true,
      });
      result.errors && console.log(result.errors);
      // Then there should be no errors in the response
      expect(result.errors).to.not.exist;
      // And then there should be four expenses in total since we're
      // counting the two collectives hosted by `hostCollective`.
      expect(result.data.allExpenses).to.have.length(4);
      expect(result.data.allExpenses.map(e => e.collective.slug)).to.deep.equal([
        'brusselstogether',
        'brusselstogether',<commit_msg>test(expenses): make sure we only get expenses from approved hosted collectives<commit_after>/** @module test/graphql.expenses.test
 *
 * This tests all the GraphQL API methods that interact with
 * expenses. */

/* Test libraries */
import sinon from 'sinon';
import { expect } from 'chai';

/* Test utilities */
import * as utils from './utils';
import * as store from './features/support/stores';

/* Support code */
import models from '../server/models';
import emailLib from '../server/lib/email';
import * as libtransactions from '../server/lib/transactions';
import { getFxRate } from '../server/lib/currency';

import paypalAdaptive from '../server/paymentProviders/paypal/adaptiveGateway';

/* Queries used throughout these tests */
const allExpensesQuery = `
  query allExpenses($CollectiveId: Int!, $category: String, $fromCollectiveSlug: String, $limit: Int, $includeHostedCollectives: Boolean) {
    allExpenses(CollectiveId: $CollectiveId, category: $category, fromCollectiveSlug: $fromCollectiveSlug, limit: $limit, includeHostedCollectives: $includeHostedCollectives) {
      id
      description
      amount
      category
      user { id email paypalEmail collective { id slug } }
      collective { id slug } } }`;

const expensesQuery = `
  query expenses($CollectiveId: Int, $CollectiveSlug: String, $category: String, $FromCollectiveId: Int, $FromCollectiveSlug: String, $status: ExpenseStatus, $offset: Int, $limit: Int, $orderBy: OrderByType) {
    expenses(CollectiveId: $CollectiveId, CollectiveSlug: $CollectiveSlug, category: $category, FromCollectiveId: $FromCollectiveId, FromCollectiveSlug: $FromCollectiveSlug, status: $status, offset: $offset, limit: $limit, orderBy: $orderBy) {
      expenses {
        id
        description
        amount
        category
        user { id email paypalEmail collective { id slug } }
        collective { id slug }
      }
    }
  }
`;

const createExpenseQuery = `
  mutation createExpense($expense: ExpenseInputType!) {
    createExpense(expense: $expense) {
      id
      status
      user { id name collective { id name slug } } } }`;

const approveExpenseQuery = `
  mutation approveExpense($id: Int!) {
    approveExpense(id: $id) { id status } }`;

const deleteExpenseQuery = `
  mutation deleteExpense($id: Int!) { deleteExpense(id: $id) { id } }`;

const payExpenseQuery = `
  mutation payExpense($id: Int!, $paymentProcessorFeeInCollectiveCurrency: Int, $hostFeeInCollectiveCurrency: Int, $platformFeeInCollectiveCurrency: Int) {
    payExpense(id: $id, paymentProcessorFeeInCollectiveCurrency: $paymentProcessorFeeInCollectiveCurrency, hostFeeInCollectiveCurrency: $hostFeeInCollectiveCurrency, platformFeeInCollectiveCurrency: $platformFeeInCollectiveCurrency) { id status } }`;

describe('GraphQL Expenses API', () => {
  beforeEach(utils.resetTestDB);

  beforeEach(utils.resetCaches);

  describe('#allExpenses', () => {
    it('fails if collective not found', async () => {
      // Given we have no collective with id 999999
      // When we try to retrieve expenses from an invalid collective
      const result = await utils.graphqlQuery(allExpensesQuery, {
        CollectiveId: 999999,
      });
      // Then we should see an error
      expect(result.errors).to.exist;
      // And then the error message should be appropriate
      expect(result.errors[0].message).to.equal('Collective not found');
    }); /* End of "fails if collective not found" */

    it('gets no expenses if collective has no expenses', async () => {
      // Given that we have a collective with no expenses
      const { collective } = await store.newCollectiveWithHost('railsgirlsatl', 'USD', 'USD', 10);
      // When we retrieve all its expenses
      const result = await utils.graphqlQuery(allExpensesQuery, {
        CollectiveId: collective.id,
      });
      result.errors && console.log(result.errors);
      // Then there should be no errors
      expect(result.errors).to.not.exist;
      // And then it should retrieve no expenses
      expect(result.data.allExpenses).to.have.length(0);
    }); /* End of "gets no expenses if collective has no expenses" */

    it('gets the latest expenses from one collective', async () => {
      // Given that we have a collective
      const { hostAdmin, collective } = await store.newCollectiveWithHost('railsgirlsatl', 'USD', 'USD', 10);
      // And given the above collective has some expenses
      const data = {
        currency: 'USD',
        payoutMethod: 'manual',
        collective: { id: collective.id },
      };
      await store.createExpense(hostAdmin, {
        amount: 1000,
        description: 'Pizza',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 2000,
        description: 'Beer',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 3000,
        description: 'Banner',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 4000,
        description: 'Stickers',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 5000,
        description: 'T-shirts',
        ...data,
      });
      // When we retrieve all its expenses
      const result = await utils.graphqlQuery(allExpensesQuery, {
        CollectiveId: collective.id,
        limit: 5,
      });
      result.errors && console.log(result.errors);
      expect(result.errors).to.not.exist;
      // Then it should retrieve the right amount of expenses
      const expenses = result.data.allExpenses;
      expect(expenses).to.have.length(5);
      // And then the expenses retrieved should come from the same
      // collective
      expect(expenses.map(e => e.collective.slug)).to.deep.equal([
        'railsgirlsatl',
        'railsgirlsatl',
        'railsgirlsatl',
        'railsgirlsatl',
        'railsgirlsatl',
      ]);
    }); /* End of "gets the latest expenses from one collective" */

    it('gets the latest expenses from all the hosted collectives', async () => {
      // Given that we have two collectives within a host
      const { hostAdmin, hostCollective, collective } = await store.newCollectiveWithHost('apex', 'USD', 'USD', 10);
      const anotherCollective = (await store.newCollectiveInHost('brusselstogether', 'USD', hostCollective)).collective;
      const inactiveCollective = (await store.newCollectiveInHost('womer-inactive', 'USD', hostCollective)).collective;
      inactiveCollective.isActive = false;
      await inactiveCollective.save();
      // And given that the first collective created above have two expenses
      const data = {
        currency: 'USD',
        payoutMethod: 'manual',
        collective: { id: collective.id },
      };
      await store.createExpense(hostAdmin, {
        amount: 1000,
        description: 'Pizza',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 2000,
        description: 'Beer',
        ...data,
      });
      // And given that the second collective created above also have
      // two expenses
      data.collective = { id: anotherCollective.id };
      await store.createExpense(hostAdmin, {
        amount: 3000,
        description: 'Banner',
        ...data,
      });
      await store.createExpense(hostAdmin, {
        amount: 4000,
        description: 'Stickers',
        ...data,
      });
      // And given that the inactive collective created above also has
      // one expense
      data.collective = { id: inactiveCollective.id };
      await store.createExpense(hostAdmin, {
        amount: 3500,
        description: 'Banner inactive',
        ...data,
      });
      // When we retrieve all the expenses of the host
      const result = await utils.graphqlQuery(allExpensesQuery, {
        CollectiveId: hostCollective.id,
        limit: 5,
        includeHostedCollectives: true,
      });
      result.errors && console.log(result.errors);
      // Then there should be no errors in the response
      expect(result.errors).to.not.exist;
      // And then there should be four expenses in total since we're
      // counting the two collectives hosted by `hostCollective`.
      expect(result.data.allExpenses).to.have.length(4);
      expect(result.data.allExpenses.map(e => e.collective.slug)).to.deep.equal([
        'brusselstogether',
        'brusselstogether',
{'fraction_non_alphanumeric': 0.11500709920365455, 'fraction_numerical': 0.007654793505771962, 'mean_word_length': 2.877453326950694, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2949696', 'n_tokens_mistral': 4702, 'n_tokens_neox': 4561, 'n_words': 1274}