<commit_before>import React from 'react'
import PropTypes from 'prop-types'

import { compose, bindActionCreators } from 'redux'
import { connect } from 'react-redux'

import { Row, Col, Form, Input, Button, message } from 'antd'
const FormItem = Form.Item
const connectForm = Form.create()

import { layout, feedback, help, rules, disableSubmit } from '../../../../util/form'
import api from '../../../../services'

const impactFields = [
  {
    field: 'overview.impact.academic',
    title: 'Academic Experience',
    subtitle: 'How will this project enrich a student’s learning environment and experience?'
  }, {
    field: 'overview.impact.research',
    title: 'Research Involvement',
    subtitle: 'Can this project be used for scholarly research?'
  }, {
    field: 'overview.impact.career',
    title: 'Career Development',
    subtitle: 'Can this technology be used to further a student’s career?'
  }
]
const projectFields = [
  {
    title: 'State Analysis',
    subtitle: 'Tell us about...',
    current: {
      field: 'plan.state.current',
      title: 'Current State',
      subtitle: 'Describe the resources currently available, and how students utilize these resources.'
    },
    future: {
      field: 'plan.state.future',
      title: 'Future State',
      subtitle: 'Describe the new resources being made available to students, including any advantages over current technology.'
    }
  },
  {
    title: 'Service Availability',
    subtitle: 'What sort of technology is currently available, and how will this change?',
    current: {
      field: 'plan.availability.current',
      title: 'Current Availability',
      subtitle: 'How many students use these resources per quarter? Include any restrictions that are involved, including hours of operations, mandatory training, and associations with organizations'
    },
    future: {
      field: 'plan.availability.future',
      title: 'Future Availability',
      subtitle: 'How will students be able to use the proposed resources? Include any restrictions that are involved, including hours of operations, mandatory training, and associations with organizations.'
    }
  },
  {
    title: 'Student Outreach',
    subtitle: 'Student technology is great, but we also need to connect with the community. How are you working to make sure students are aware of these resources?',
    current: {
      field: 'plan.outreach.current',
      title: 'Prior Efforts',
      subtitle: 'Describe efforts to receive departmental funding and community endorsements for this project and similar initiatives.'
    },
    future: {
      field: 'plan.outreach.future',
      title: 'Outreach Strategy',
      subtitle: 'If approved, how will you reach out to the student community and inform them about this resource.'
    }
  },
  {
    title: 'Implementation Strategy',
    subtitle: 'What sort of support have you gathered?',
    current: {
      field: 'plan.strategy.current',
      title: 'Organizational Backing',
      subtitle: 'What sort of resources will your organization make available to ensure the success of this project?'
    },
    future: {
      field: 'plan.strategy.future',
      title: 'Implementation Strategy',
      subtitle: 'Briefly describe the implementation process, and include a timeline if possible.'
    }
  },
  {
    title: 'Risk Assessment',
    subtitle: 'How are we accounting for scenarios such as abuse, theft, or negigent use of technology?',
    current: {
      field: 'plan.risk.current',
      title: 'Current Risks',
      subtitle: 'Are there any current concerns regarding privacy or security? Have you scoped out insurance for the proposed technology?'
    },
    future: {
      field: 'plan.risk.future',
      title: 'Proposed Mitigations',
      subtitle: 'If approved, how will you secure the systems and technology for this project.'
    }
  }
]

@compose(
  connect(
    state => ({
      parent: state.db.proposal._id,
      body: state.db.proposal.body
      //  Direct accessing subdoc props that may not exist === crash
    }),
    dispatch => ({ api: bindActionCreators(api, dispatch) })
  ),
  connectForm
)
class ProposalBody extends React.Component {
  componentDidUpdate (prevProps, prevState) {
    //  Load fields from server
    if (!prevProps.parent && this.props.parent) {
      this.props.form.validateFields()
    }
  }
  // componentDidUpdate (prevProps, prevState) {
  //   //  Load fields from server
  //   console.log('REACHED UPDATE')
  //   if (!prevProps.parent && this.props.parent) {
  //     const { overview, plan } = this.props
  //     console.log(...[overview, plan])
  //     this.props.form.setFieldsValue(...[overview, plan])
  //     //  Run validation, disabling submit buttons
  //     this.props.form.validateFields()
  //   }
  // }
  handleSubmit = (e) => {
    e.preventDefault()
    let { form, api, parent } = this.props
    form.validateFields((err, values) => {
      if (!err) {
        console.log(values)
        api.put('project', { proposal: parent, ...values })
        .then(message.success('Proposal Body updated!'))
        .catch(err => {
          message.warning('Proposal Body failed to update - Unexpected client error')
          console.warn(err)
        })
      }
    })
  }

  // render ({ form, overview = {}, plan = {} } = this.props) {
  render ({ form, body } = this.props) {
    // console.log('DATA IN:', overview, plan)
    return (

        <Form onSubmit={this.handleSubmit}>

Overview

          <FormItem label='Abstract' {...layout} hasFeedback={feedback(form, 'overview.abstract')} help={help(form, 'overview.abstract')} >
            {form.getFieldDecorator('overview.abstract', rules.required)(
              <Input type='textarea' rows={6} />
            )}
          </FormItem>
          <FormItem label='Objectives' {...layout} hasFeedback={feedback(form, 'overview.objectives')} help={help(form, 'overview.objectives')} >
            {form.getFieldDecorator('overview.objectives', rules.required)(
              <Input type='textarea' rows={4} />
            )}
          </FormItem>
          <FormItem label='Core Justification' {...layout} hasFeedback={feedback(form, 'overview.justification')} help={help(form, 'overview.justification')} >
            {form.getFieldDecorator('overview.justification', rules.required)(
              <Input type='textarea' rows={4} />
            )}
          </FormItem>

Impact

          <Row gutter={32}>
            {impactFields.map((impact, i) => (
              <Col key={i} className='gutter-row' xs={24} md={8}>

{impact.title}

{impact.subtitle}

                <FormItem hasFeedback={feedback(form, impact.field)} help={help(form, impact.field)} >
                  {form.getFieldDecorator(impact.field, rules.required)(
                    <Input type='textarea' rows={3} />
                  )}
                </FormItem>
              </Col>
            ))}
          </Row>

Project Plan

          {projectFields.map((area, i) => (

{area.title}

{area.subtitle}

              <Row gutter={32}>
                <Col className='gutter-row' xs={24} md={12} >

{area.current.title}

                  {area.current.subtitle}
                  <FormItem hasFeedback={feedback(form, area.current.field)} help={help(form, area.current.field)} >
                    {form.getFieldDecorator(area.current.field, rules.required)(
                      <Input type='textarea' rows={4} />
                    )}
                  </FormItem>
                </Col>
                <Col className='gutter-row' xs={24} md={12} >

{area.future.title}

                  {area.future.subtitle}
                  <FormItem hasFeedback={feedback(form, area.future.field)} help={help(form, area.future.field)} >
                    {form.getFieldDecorator(area.future.field, rules.required)(
                      <Input type='textarea' rows={4} />
                    )}
                  </FormItem>
                </Col>
              </Row>

          ))}
          <FormItem>
            <Button size='large' type='primary'
              htmlType='submit' disabled={disableSubmit(form)}
              >Update</Button>
          </FormItem>
        </Form>

    )
  }
}

ProposalBody.propTypes = {
  form: PropTypes.object,
  api: PropTypes.object,
  parent: PropTypes.string
}
export default ProposalBody
<commit_msg>Put or POST<commit_after>import React from 'react'
import PropTypes from 'prop-types'

import { compose, bindActionCreators } from 'redux'
import { connect } from 'react-redux'

import { Row, Col, Form, Input, Button, message } from 'antd'
const FormItem = Form.Item
const connectForm = Form.create()

import { layout, feedback, help, rules, disableSubmit } from '../../../../util/form'
import api from '../../../../services'

const impactFields = [
  {
    field: 'overview.impact.academic',
    title: 'Academic Experience',
    subtitle: 'How will this project enrich a student’s learning environment and experience?'
  }, {
    field: 'overview.impact.research',
    title: 'Research Involvement',
    subtitle: 'Can this project be used for scholarly research?'
  }, {
    field: 'overview.impact.career',
    title: 'Career Development',
    subtitle: 'Can this technology be used to further a student’s career?'
  }
]
const projectFields = [
  {
    title: 'State Analysis',
    subtitle: 'Tell us about...',
    current: {
      field: 'plan.state.current',
      title: 'Current State',
      subtitle: 'Describe the resources currently available, and how students utilize these resources.'
    },
    future: {
      field: 'plan.state.future',
      title: 'Future State',
      subtitle: 'Describe the new resources being made available to students, including any advantages over current technology.'
    }
  },
  {
    title: 'Service Availability',
    subtitle: 'What sort of technology is currently available, and how will this change?',
    current: {
      field: 'plan.availability.current',
      title: 'Current Availability',
      subtitle: 'How many students use these resources per quarter? Include any restrictions that are involved, including hours of operations, mandatory training, and associations with organizations'
    },
    future: {
      field: 'plan.availability.future',
      title: 'Future Availability',
      subtitle: 'How will students be able to use the proposed resources? Include any restrictions that are involved, including hours of operations, mandatory training, and associations with organizations.'
    }
  },
  {
    title: 'Student Outreach',
    subtitle: 'Student technology is great, but we also need to connect with the community. How are you working to make sure students are aware of these resources?',
    current: {
      field: 'plan.outreach.current',
      title: 'Prior Efforts',
      subtitle: 'Describe efforts to receive departmental funding and community endorsements for this project and similar initiatives.'
    },
    future: {
      field: 'plan.outreach.future',
      title: 'Outreach Strategy',
      subtitle: 'If approved, how will you reach out to the student community and inform them about this resource.'
    }
  },
  {
    title: 'Implementation Strategy',
    subtitle: 'What sort of support have you gathered?',
    current: {
      field: 'plan.strategy.current',
      title: 'Organizational Backing',
      subtitle: 'What sort of resources will your organization make available to ensure the success of this project?'
    },
    future: {
      field: 'plan.strategy.future',
      title: 'Implementation Strategy',
      subtitle: 'Briefly describe the implementation process, and include a timeline if possible.'
    }
  },
  {
    title: 'Risk Assessment',
    subtitle: 'How are we accounting for scenarios such as abuse, theft, or negigent use of technology?',
    current: {
      field: 'plan.risk.current',
      title: 'Current Risks',
      subtitle: 'Are there any current concerns regarding privacy or security? Have you scoped out insurance for the proposed technology?'
    },
    future: {
      field: 'plan.risk.future',
      title: 'Proposed Mitigations',
      subtitle: 'If approved, how will you secure the systems and technology for this project.'
    }
  }
]

@compose(
  connect(
    state => ({
      parent: state.db.proposal._id,
      body: state.db.proposal.body
      //  Direct accessing subdoc props that may not exist === crash
    }),
    dispatch => ({ api: bindActionCreators(api, dispatch) })
  ),
  connectForm
)
class ProposalBody extends React.Component {
  componentDidUpdate (prevProps, prevState) {
    //  Load fields from server
    if (!prevProps.parent && this.props.parent) {
      this.props.form.validateFields()
    }
  }
  // componentDidUpdate (prevProps, prevState) {
  //   //  Load fields from server
  //   console.log('REACHED UPDATE')
  //   if (!prevProps.parent && this.props.parent) {
  //     const { overview, plan } = this.props
  //     console.log(...[overview, plan])
  //     this.props.form.setFieldsValue(...[overview, plan])
  //     //  Run validation, disabling submit buttons
  //     this.props.form.validateFields()
  //   }
  // }
  handleSubmit = (e) => {
    e.preventDefault()
    let { form, api, parent, body } = this.props
    form.validateFields((err, values) => {
      if (!err) {
        console.log(values)
        //  Post to create if it doesn't exist, otherwise, update by ID
        !body
        ? api.post('project', {
          proposal: parent, ...values
        })
        : api.put('project', {
          proposal: parent, ...values
        }, {
          id: body._id
        })
        .then(message.success('Proposal Body updated!'))
        .catch(err => {
          message.warning('Proposal Body failed to update - Unexpected client error')
          console.warn(err)
        })
      }
    })
  }

  // render ({ form, overview = {}, plan = {} } = this.props) {
  render ({ form, body } = this.props) {
    // console.log('DATA IN:', overview, plan)
    return (

        <Form onSubmit={this.handleSubmit}>

Overview

          <FormItem label='Abstract' {...layout} hasFeedback={feedback(form, 'overview.abstract')} help={help(form, 'overview.abstract')} >
            {form.getFieldDecorator('overview.abstract', rules.required)(
              <Input type='textarea' rows={6} />
            )}
          </FormItem>
          <FormItem label='Objectives' {...layout} hasFeedback={feedback(form, 'overview.objectives')} help={help(form, 'overview.objectives')} >
            {form.getFieldDecorator('overview.objectives', rules.required)(
              <Input type='textarea' rows={4} />
            )}
          </FormItem>
          <FormItem label='Core Justification' {...layout} hasFeedback={feedback(form, 'overview.justification')} help={help(form, 'overview.justification')} >
            {form.getFieldDecorator('overview.justification', rules.required)(
              <Input type='textarea' rows={4} />
            )}
          </FormItem>

Impact

          <Row gutter={32}>
            {impactFields.map((impact, i) => (
              <Col key={i} className='gutter-row' xs={24} md={8}>

{impact.title}

{impact.subtitle}

                <FormItem hasFeedback={feedback(form, impact.field)} help={help(form, impact.field)} >
                  {form.getFieldDecorator(impact.field, rules.required)(
                    <Input type='textarea' rows={3} />
                  )}
                </FormItem>
              </Col>
            ))}
          </Row>

Project Plan

          {projectFields.map((area, i) => (

{area.title}

{area.subtitle}

              <Row gutter={32}>
                <Col className='gutter-row' xs={24} md={12} >

{area.current.title}

                  {area.current.subtitle}
                  <FormItem hasFeedback={feedback(form, area.current.field)} help={help(form, area.current.field)} >
                    {form.getFieldDecorator(area.current.field, rules.required)(
                      <Input type='textarea' rows={4} />
                    )}
                  </FormItem>
                </Col>
                <Col className='gutter-row' xs={24} md={12} >

{area.future.title}

                  {area.future.subtitle}
                  <FormItem hasFeedback={feedback(form, area.future.field)} help={help(form, area.future.field)} >
                    {form.getFieldDecorator(area.future.field, rules.required)(
                      <Input type='textarea' rows={4} />
                    )}
                  </FormItem>
                </Col>
              </Row>

          ))}
          <FormItem>
            <Button size='large' type='primary'
              htmlType='submit' disabled={disableSubmit(form)}
              >Update</Button>
          </FormItem>
        </Form>

    )
  }
}

ProposalBody.propTypes = {
  form: PropTypes.object,
  api: PropTypes.object,
  parent: PropTypes.string,
  body: PropTypes.object
}
export default ProposalBody
<|endoftext|>


<commit_before>Scalr.regPage('Scalr.ui.environments.platform.cloudstack', function (loadParams, moduleParams) {
	var params = moduleParams['params'];

	var isEnabledProp = moduleParams['platform'] + '.is_enabled';

	var form = Ext.create('Ext.form.Panel', {
		bodyCls: 'x-panel-body-frame',
		scalrOptions: {
			'modal': true
		},
		width: 600,
		title: 'Environments » ' + moduleParams.env.name + '» ' + moduleParams['platformName'],
		fieldDefaults: {
			anchor: '100%',
			labelWidth: 120
		},

		items: [{
			xtype: 'checkbox',
			name: isEnabledProp,
			checked: params[isEnabledProp],
			hideLabel: true,
			boxLabel: 'I want to use '+moduleParams['platformName'],
			listeners: {
				'change': function () {
					if (this.getValue()) {
						form.down('[name="api_key"]').show();
						form.down('[name="api_url"]').show();
						form.down('[name="secret_key"]').show();
					} else {
						form.down('[name="api_key"]').hide();
						form.down('[name="api_url"]').hide();
						form.down('[name="secret_key"]').hide();
					}
				}
			}
		}, {
			xtype: 'textfield',
			fieldLabel: 'API key',
			name: 'api_key',
			value: params['api_key'],
			hidden: !params[isEnabledProp]
		}, {
			xtype: 'textfield',
			fieldLabel: 'Secret key',
			name: 'secret_key',
			value: params['secret_key'],
			hidden: !params[isEnabledProp]
		}, {
			xtype: 'textfield',
			fieldLabel: 'API URL',
			name: 'api_url',
			value: params['api_url'],
			hidden: !params[isEnabledProp]
		}],

		dockedItems: [{
			xtype: 'container',
			dock: 'bottom',
			cls: 'x-docked-bottom-frame',
			layout: {
				type: 'hbox',
				pack: 'center'
			},
			items: [{
				xtype: 'button',
				text: 'Save',
				handler: function() {
					if (form.getForm().isValid()) {
						Scalr.Request({
							processBox: {
								type: 'save'
							},
							form: form.getForm(),
							params: { platform: moduleParams['platform']},
							url: '/environments/' + moduleParams.env.id + '/platform/xSaveCloudstack',
							success: function (data) {
								var flag = Scalr.flags.needEnvConfig && data.enabled;
								Scalr.event.fireEvent('update', '/environments/' + moduleParams.env.id + '/edit', moduleParams['platform'], data.enabled);
								if (! flag)
									Scalr.event.fireEvent('close');
							}
						});
					}
				}
			}, {
				xtype: 'button',
				margin: '0 0 0 5',
				hidden: Scalr.flags.needEnvConfig,
				text: 'Cancel',
				handler: function() {
					Scalr.event.fireEvent('close');
				}
			}, {
				xtype: 'button',
				hidden: !Scalr.flags.needEnvConfig,
				margin: '0 0 0 5',
				text: "I'm not using "+moduleParams['platformName']+", let me configure another cloud",
				handler: function () {
					Scalr.event.fireEvent('redirect', '#/environments/' + moduleParams.env.id + '/edit', true);
				}
			}, {
				xtype: 'button',
				hidden: !Scalr.flags.needEnvConfig,
				margin: '0 0 0 5',
				text: 'Do this later',
				handler: function () {
					sessionStorage.setItem('needEnvConfigLater', true);
					Scalr.event.fireEvent('unlock');
					Scalr.event.fireEvent('redirect', '#/dashboard');
				}
			}]
		}]
	});

	if (moduleParams['platform'] == 'idcf') {
		var apiUrl = form.down('[name="api_url"]')
		apiUrl.setValue('https://apis.i.noahcloud.jp/portal/client/api');
		//apiUrl.setReadOnly(true);
	}

	return form;
});
<commit_msg>Scalr 4.0.0<commit_after>Scalr.regPage('Scalr.ui.environments.platform.cloudstack', function (loadParams, moduleParams) {
	var params = moduleParams['params'];

	var isEnabledProp = moduleParams['platform'] + '.is_enabled';

	var form = Ext.create('Ext.form.Panel', {
		bodyCls: 'x-panel-body-frame',
		scalrOptions: {
			'modal': true
		},
		width: 600,
		title: 'Environments » ' + moduleParams.env.name + '» ' + moduleParams['platformName'],
		fieldDefaults: {
			anchor: '100%',
			labelWidth: 120
		},

		items: [{
			xtype: 'checkbox',
			name: isEnabledProp,
			checked: params[isEnabledProp],
			hideLabel: true,
			boxLabel: 'I want to use '+moduleParams['platformName'],
			listeners: {
				'change': function () {
					if (this.getValue()) {
						form.down('[name="api_key"]').show();
						form.down('[name="api_url"]').show();
						form.down('[name="secret_key"]').show();
					} else {
						form.down('[name="api_key"]').hide();
						form.down('[name="api_url"]').hide();
						form.down('[name="secret_key"]').hide();
					}
				}
			}
		}, {
			xtype: 'textfield',
			fieldLabel: 'API key',
			name: 'api_key',
			value: params['api_key'],
			hidden: !params[isEnabledProp]
		}, {
			xtype: 'textfield',
			fieldLabel: 'Secret key',
			name: 'secret_key',
			value: params['secret_key'],
			hidden: !params[isEnabledProp]
		}, {
			xtype: 'textfield',
			fieldLabel: 'API URL',
			name: 'api_url',
			value: params['api_url'],
			hidden: !params[isEnabledProp]
		}],

		dockedItems: [{
			xtype: 'container',
			dock: 'bottom',
			cls: 'x-docked-bottom-frame',
			layout: {
				type: 'hbox',
				pack: 'center'
			},
			items: [{
				xtype: 'button',
				text: 'Save',
				handler: function() {
					if (form.getForm().isValid()) {
						Scalr.Request({
							processBox: {
								type: 'save'
							},
							form: form.getForm(),
							params: { platform: moduleParams['platform']},
							url: '/environments/' + moduleParams.env.id + '/platform/xSaveCloudstack',
							success: function (data) {
								var flag = Scalr.flags.needEnvConfig && data.enabled;
								Scalr.event.fireEvent('update', '/environments/' + moduleParams.env.id + '/edit', moduleParams['platform'], data.enabled);
								if (! flag)
									Scalr.event.fireEvent('close');
							}
						});
					}
				}
			}, {
				xtype: 'button',
				margin: '0 0 0 5',
				hidden: Scalr.flags.needEnvConfig,
				text: 'Cancel',
				handler: function() {
					Scalr.event.fireEvent('close');
				}
			}, {
				xtype: 'button',
				hidden: !Scalr.flags.needEnvConfig,
				margin: '0 0 0 5',
				text: "I'm not using "+moduleParams['platformName']+", let me configure another cloud",
				handler: function () {
					Scalr.event.fireEvent('redirect', '#/environments/' + moduleParams.env.id + '/edit', true);
				}
			}, {
				xtype: 'button',
				hidden: !Scalr.flags.needEnvConfig,
				margin: '0 0 0 5',
				text: 'Do this later',
				handler: function () {
					sessionStorage.setItem('needEnvConfigLater', true);
					Scalr.event.fireEvent('unlock');
					Scalr.event.fireEvent('redirect', '#/dashboard');
				}
			}]
		}]
	});

	if (moduleParams['platform'] == 'idcf') {
		var apiUrl = form.down('[name="api_url"]')
		apiUrl.setValue('https://api.noahcloud.jp/portal/client/api');
		//apiUrl.setReadOnly(true);
	}

	return form;
});
<|endoftext|>


<commit_before>// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

import {
  composePreprocessors,
  preprocessArray,
  interpolateScript,
  interpolateString,
} from '../src/preprocessors'
import Variables from '../src/Variables'
import FakeExecutor from './util/FakeExecutor'

let variables
beforeEach(() => {
  variables = new Variables()
})

describe('preprocessor composition', () => {
  it('should do nothing if the command takes no arguments', () => {
    const fn = jest.fn()
    const f = composePreprocessors(fn)
    f()
    expect(fn).toHaveBeenCalledWith()
  })
  it('should compose target preprocessor', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(interpolateString, fn)
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}')
    expect(fn).toHaveBeenCalledWith('a')
  })
  it('should compose value preprocessor', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(
      interpolateString,
      interpolateString,
      fn
    )
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}', '${a}')
    expect(fn).toHaveBeenCalledWith('a', 'a')
  })
  it('should compose targets preprocessor', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(
      interpolateString,
      interpolateString,
      { targets: preprocessArray(interpolateString) },
      fn
    )
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}', '${a}', { targets: [['${a}', 's'], ['${a}', 's']] })
    expect(fn).toHaveBeenCalledWith('a', 'a', { targets: [['a', 's'], ['a', 's']] })
  })
  it('should ignore preprocess if null is passed', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(
      interpolateString,
      null,
      fn
    )
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}', '${a}')
    expect(fn).toHaveBeenCalledWith('a', '${a}')
  })
  it('should skip targets preprocessing if it is undefined', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(
      interpolateString,
      interpolateString,
      preprocessArray(interpolateString),
      fn
    )
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}', '${a}')
    expect(fn).toHaveBeenCalledWith('a', 'a')
  })
})

describe('array preprocessing', () => {
  it('should preprocess array of string using a single interpolator', () => {
    variables.set('a', 'a')
    const arr = [['${a}a', 's'], ['${a}b', 's']]
    expect(preprocessArray(interpolateString)(arr, variables)).toEqual([
      ['aa', 's'],
      ['ab', 's'],
    ])
  })
})

describe('interpolate string', () => {
  it('should interpolate false values', () => {
    variables.set('a', undefined)
    expect(interpolateString('${a}', variables)).toBe('undefined')
    variables.set('a', null)
    expect(interpolateString('${a}', variables)).toBe('null')
    variables.set('a', false)
    expect(interpolateString('${a}', variables)).toBe('false')
    variables.set('a', 0)
    expect(interpolateString('${a}', variables)).toBe('0')
    variables.set('a', '')
    expect(interpolateString('${a}', variables)).toBe('')
  })
})

describe('interpolate script', () => {
  it('should not interpolate a script without variables', () => {
    const script = 'return 1'
    expect(interpolateScript(script, variables).script).toEqual(script)
  })
  it('should interpolate a script with a single argument', () => {
    variables.set('a', 1)
    const script = 'return ${a}'
    const r = interpolateScript(script, variables)
    expect(r.script).toEqual('return arguments[0]')
    expect(r.argv[0]).toBe(1)
  })
  it('should interpolate a script with multiple arguments', () => {
    variables.set('a', 1)
    variables.set('b', false)
    const script = 'return ${a} + ${a} || ${b}'
    const r = interpolateScript(script, variables)
    expect(r.script).toEqual(
      'return arguments[0] + arguments[0] || arguments[1]'
    )
    expect(r.argv[0]).toBe(1)
    expect(r.argv[1]).toBe(false)
  })
})
<commit_msg>fix lint<commit_after>// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

import {
  composePreprocessors,
  preprocessArray,
  interpolateScript,
  interpolateString,
} from '../src/preprocessors'
import Variables from '../src/Variables'
import FakeExecutor from './util/FakeExecutor'

let variables
beforeEach(() => {
  variables = new Variables()
})

describe('preprocessor composition', () => {
  it('should do nothing if the command takes no arguments', () => {
    const fn = jest.fn()
    const f = composePreprocessors(fn)
    f()
    expect(fn).toHaveBeenCalledWith()
  })
  it('should compose target preprocessor', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(interpolateString, fn)
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}')
    expect(fn).toHaveBeenCalledWith('a')
  })
  it('should compose value preprocessor', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(
      interpolateString,
      interpolateString,
      fn
    )
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}', '${a}')
    expect(fn).toHaveBeenCalledWith('a', 'a')
  })
  it('should compose targets preprocessor', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(
      interpolateString,
      interpolateString,
      { targets: preprocessArray(interpolateString) },
      fn
    )
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}', '${a}', { targets: [['${a}', 's'], ['${a}', 's']] })
    expect(fn).toHaveBeenCalledWith('a', 'a', {
      targets: [['a', 's'], ['a', 's']],
    })
  })
  it('should ignore preprocess if null is passed', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(
      interpolateString,
      null,
      fn
    )
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}', '${a}')
    expect(fn).toHaveBeenCalledWith('a', '${a}')
  })
  it('should skip targets preprocessing if it is undefined', () => {
    variables.set('a', 'a')
    const fn = jest.fn()
    FakeExecutor.prototype.doFake = composePreprocessors(
      interpolateString,
      interpolateString,
      preprocessArray(interpolateString),
      fn
    )
    const exec = new FakeExecutor()
    exec.init({ variables })
    exec.doFake('${a}', '${a}')
    expect(fn).toHaveBeenCalledWith('a', 'a')
  })
})

describe('array preprocessing', () => {
  it('should preprocess array of string using a single interpolator', () => {
    variables.set('a', 'a')
    const arr = [['${a}a', 's'], ['${a}b', 's']]
    expect(preprocessArray(interpolateString)(arr, variables)).toEqual([
      ['aa', 's'],
      ['ab', 's'],
    ])
  })
})

describe('interpolate string', () => {
  it('should interpolate false values', () => {
    variables.set('a', undefined)
    expect(interpolateString('${a}', variables)).toBe('undefined')
    variables.set('a', null)
    expect(interpolateString('${a}', variables)).toBe('null')
    variables.set('a', false)
    expect(interpolateString('${a}', variables)).toBe('false')
    variables.set('a', 0)
    expect(interpolateString('${a}', variables)).toBe('0')
    variables.set('a', '')
    expect(interpolateString('${a}', variables)).toBe('')
  })
})

describe('interpolate script', () => {
  it('should not interpolate a script without variables', () => {
    const script = 'return 1'
    expect(interpolateScript(script, variables).script).toEqual(script)
  })
  it('should interpolate a script with a single argument', () => {
    variables.set('a', 1)
    const script = 'return ${a}'
    const r = interpolateScript(script, variables)
    expect(r.script).toEqual('return arguments[0]')
    expect(r.argv[0]).toBe(1)
  })
  it('should interpolate a script with multiple arguments', () => {
    variables.set('a', 1)
    variables.set('b', false)
    const script = 'return ${a} + ${a} || ${b}'
    const r = interpolateScript(script, variables)
    expect(r.script).toEqual(
      'return arguments[0] + arguments[0] || arguments[1]'
    )
    expect(r.argv[0]).toBe(1)
    expect(r.argv[1]).toBe(false)
  })
})
<|endoftext|>


<commit_before>/*global define*/
define([
    'underscore',
    'app',
    'marionette',
    'models/note',
    'collections/notebooks',
    'apps/notes/show/noteView'
], function (_, App, Marionette, NoteModel, NotebooksCollection, NoteView) {
    'use strict';

    var Show = App.module('AppNote.Show');

    /**
     * Controller shows note's content in App.content
     */
    Show.Controller = Marionette.Controller.extend({
        initialize: function () {
            _.bindAll(this, 'showNote', 'showContent');

            // Re render note's content after sync event
            App.on('sync:after', this.syncRender, this);
        },

        syncRender: function (collectionName) {
            if (collectionName !== 'notes') {
                this.showNote();
            }
        },

        /**
         * Fetch note, then show it
         */
        showNote: function (args) {
            this.args = args || this.args;

            this.note = new NoteModel({ id : this.args.id });
            this.notebooks = new NotebooksCollection();

            // Events
            this.note.on('updateTaskProgress', this.updateTaskProgress, this);
            this.note.on('change', this.triggerChangeToSidebar, this);

            $.when(this.note.fetch(), this.notebooks.fetch()).done(this.showContent);
        },

        showContent: function () {
            var notebook = this.notebooks.get(this.note.get('notebookId')),
                decrypted,
                args;

            decrypted = {
                title   : App.Encryption.API.decrypt(this.note.get('title')),
                content : App.Encryption.API.decrypt(this.note.get('content')),
                notebook: null
            };

            if (notebook) {
                decrypted.notebook = notebook.get('name');
            }

            args = {
                model     : this.note,
                decrypted : decrypted,
                args      : this.args
            };

            App.content.show(new NoteView(args));
        },

        triggerChangeToSidebar: function () {
            App.trigger('notes:changeModel', this.note.get('id'));
        },

        updateTaskProgress: function (text) {
            var content = App.Encryption.API.encrypt(text.content);
            this.note.trigger('update:any');

            this.note.save({
                content       : content,
                taskCompleted : text.completed
            });
        }

    });

    return Show.Controller;
});
<commit_msg>sync bug<commit_after>/*global define*/
define([
    'underscore',
    'app',
    'marionette',
    'models/note',
    'collections/notebooks',
    'apps/notes/show/noteView'
], function (_, App, Marionette, NoteModel, NotebooksCollection, NoteView) {
    'use strict';

    var Show = App.module('AppNote.Show');

    /**
     * Controller shows note's content in App.content
     */
    Show.Controller = Marionette.Controller.extend({
        initialize: function () {
            _.bindAll(this, 'showNote', 'showContent');

            // Re render note's content after sync event
            App.on('sync:after', this.syncRender, this);
        },

        syncRender: function (collectionName) {
            if (collectionName === 'notes') {
                this.showNote();
            }
        },

        /**
         * Fetch note, then show it
         */
        showNote: function (args) {
            this.args = args || this.args;

            this.note = new NoteModel({ id : this.args.id });
            this.notebooks = new NotebooksCollection();

            // Events
            this.note.on('updateTaskProgress', this.updateTaskProgress, this);
            this.note.on('change', this.triggerChangeToSidebar, this);

            $.when(this.note.fetch(), this.notebooks.fetch()).done(this.showContent);
        },

        showContent: function () {
            var notebook = this.notebooks.get(this.note.get('notebookId')),
                decrypted,
                args;

            decrypted = {
                title   : App.Encryption.API.decrypt(this.note.get('title')),
                content : App.Encryption.API.decrypt(this.note.get('content')),
                notebook: null
            };

            if (notebook) {
                decrypted.notebook = notebook.get('name');
            }

            args = {
                model     : this.note,
                decrypted : decrypted,
                args      : this.args
            };

            App.content.show(new NoteView(args));
        },

        triggerChangeToSidebar: function () {
            App.trigger('notes:changeModel', this.note.get('id'));
        },

        updateTaskProgress: function (text) {
            var content = App.Encryption.API.encrypt(text.content);
            this.note.trigger('update:any');

            this.note.save({
                content       : content,
                taskCompleted : text.completed
            });
        }

    });

    return Show.Controller;
});
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const roleService = require('../role');
const { SUPER_ADMIN_CODE } = require('../constants');

describe('Role', () => {
  describe('create', () => {
    test('Creates a role', async () => {
      const dbCreate = jest.fn(role => Promise.resolve(role));
      const dbCount = jest.fn(() => Promise.resolve(0));

      global.strapi = {
        query: () => ({ create: dbCreate, count: dbCount }),
      };

      const input = {
        name: 'super_admin',
        description: "Have all permissions. Can't be delete",
        code: 'super-admin',
      };

      const createdRole = await roleService.create(input);

      expect(dbCreate).toHaveBeenCalledWith(input);
      expect(createdRole).toStrictEqual(input);
    });
  });
  describe('findOne', () => {
    test('Finds a role', async () => {
      const role = {
        id: 1,
        name: 'super_admin',
        description: "Have all permissions. Can't be delete",
      };
      const dbFindOne = jest.fn(({ id }) => Promise.resolve(_.find([role], { id })));

      global.strapi = {
        query: () => ({ findOne: dbFindOne }),
      };

      const foundRole = await roleService.findOne({ id: role.id });

      expect(dbFindOne).toHaveBeenCalledWith({ id: role.id }, []);
      expect(foundRole).toStrictEqual(role);
    });
    test('Finds a role with usersCount', async () => {
      const role = {
        id: 1,
        name: 'super_admin',
        description: "Have all permissions. Can't be delete",
        usersCount: 0,
      };
      const dbFindOne = jest.fn(({ id }) =>
        Promise.resolve(_.find([_.omit(role, ['usersCount'])], { id }))
      );
      const dbCount = jest.fn(() => Promise.resolve(0));
      global.strapi = {
        query: () => ({ findOne: dbFindOne, count: dbCount }),
      };

      const foundRole = await roleService.findOneWithUsersCount({ id: role.id });

      expect(dbFindOne).toHaveBeenCalledWith({ id: role.id }, []);
      expect(dbCount).toHaveBeenCalledWith({ roles: [role.id] });
      expect(foundRole).toStrictEqual(role);
    });
  });
  describe('find', () => {
    test('Finds roles', async () => {
      const roles = [
        {
          id: 1,
          name: 'super_admin',
          description: "Have all permissions. Can't be delete",
        },
      ];
      const dbFind = jest.fn(() => Promise.resolve(roles));

      global.strapi = {
        query: () => ({ find: dbFind }),
      };

      const foundRoles = await roleService.find();

      expect(dbFind).toHaveBeenCalledWith({}, []);
      expect(foundRoles).toStrictEqual(roles);
    });
  });
  describe('findAll', () => {
    test('Finds all roles', async () => {
      const roles = [
        {
          id: 1,
          name: 'super_admin',
          description: "Have all permissions. Can't be delete",
          usersCount: 0,
        },
      ];
      const dbFind = jest.fn(() =>
        Promise.resolve(roles.map(role => _.omit(role, ['usersCount'])))
      );
      const dbCount = jest.fn(() => Promise.resolve(0));

      global.strapi = {
        query: () => ({ find: dbFind, count: dbCount }),
      };

      const foundRoles = await roleService.findAllWithUsersCount();

      expect(dbFind).toHaveBeenCalledWith({ _limit: -1 }, []);
      expect(foundRoles).toStrictEqual(roles);
    });
  });
  describe('update', () => {
    test('Updates a role', async () => {
      const role = {
        id: 1,
        name: 'super_admin',
        description: 'AAA',
      };
      const expectedUpdatedRole = {
        id: 1,
        name: 'super_admin_updated',
        description: 'AAA_updated',
      };
      const dbUpdate = jest.fn(() => Promise.resolve(expectedUpdatedRole));
      const dbCount = jest.fn(() => Promise.resolve(0));

      global.strapi = {
        query: () => ({ update: dbUpdate, count: dbCount }),
      };

      const updatedRole = await roleService.update(
        {
          id: role.id,
        },
        {
          name: expectedUpdatedRole.name,
          description: expectedUpdatedRole.description,
        }
      );

      expect(dbUpdate).toHaveBeenCalledWith(
        {
          id: role.id,
        },
        {
          name: expectedUpdatedRole.name,
          description: expectedUpdatedRole.description,
        }
      );
      expect(updatedRole).toStrictEqual(expectedUpdatedRole);
    });
    test('Cannot update code of super admin role', async () => {
      const dbUpdate = jest.fn();
      const dbFind = jest.fn(() => [{ id: '1' }]);
      const dbFindOne = jest.fn(() => ({ id: '1', code: SUPER_ADMIN_CODE }));
      const badRequest = jest.fn(() => {});

      global.strapi = {
        query: () => ({ find: dbFind, findOne: dbFindOne, update: dbUpdate }),
        admin: { config: { superAdminCode: SUPER_ADMIN_CODE } },
        errors: { badRequest },
      };

      await roleService.update({ id: 1 }, { code: 'new_code' });

      expect(dbUpdate).toHaveBeenCalledWith({ id: 1 }, {});
    });
  });
  describe('count', () => {
    test('getUsersCount', async () => {
      const roleId = 1;
      const dbCount = jest.fn(() => Promise.resolve(0));
      global.strapi = {
        query: () => ({ count: dbCount }),
      };

      const usersCount = await roleService.getUsersCount(roleId);

      expect(dbCount).toHaveBeenCalledWith({ roles: [roleId] });
      expect(usersCount).toEqual(0);
    });
  });
  describe('delete', () => {
    test('Delete a role', async () => {
      const role = {
        id: 3,
        name: 'admin',
        description: 'Description',
        users: [],
      };
      const dbCount = jest.fn(() => Promise.resolve(0));
      const dbFindOne = jest.fn(() => ({ id: 1, code: SUPER_ADMIN_CODE }));
      const dbDelete = jest.fn(() => Promise.resolve(role));
      const dbDeleteByRolesIds = jest.fn(() => Promise.resolve());

      global.strapi = {
        query: () => ({ delete: dbDelete, count: dbCount, findOne: dbFindOne }),
        admin: {
          services: {
            permission: { deleteByRolesIds: dbDeleteByRolesIds },
          },
          config: { superAdminCode: SUPER_ADMIN_CODE },
        },
      };

      const deletedRoles = await roleService.deleteByIds([role.id]);

      expect(dbCount).toHaveBeenCalledWith({ roles: [role.id] });
      expect(dbDelete).toHaveBeenCalledWith({ id_in: [role.id] });
      expect(deletedRoles).toStrictEqual([role]);
    });
    test('Delete two roles', async () => {
      const roles = [
        {
          id: 1,
          name: 'admin 1',
          description: 'Description',
          users: [],
        },
        {
          id: 2,
          name: 'admin 2',
          description: 'Description',
          users: [],
        },
      ];
      const dbCount = jest.fn(() => Promise.resolve(0));
      const dbFindOne = jest.fn(() => ({ id: 3, code: SUPER_ADMIN_CODE }));
      const rolesIds = roles.map(r => r.id);
      const dbDelete = jest.fn(() => Promise.resolve(roles));
      const dbGetUsersCount = jest.fn(() => Promise.resolve(0));
      const dbDeleteByRolesIds = jest.fn(() => Promise.resolve());

      global.strapi = {
        query: () => ({ delete: dbDelete, count: dbCount, findOne: dbFindOne }),
        admin: {
          services: {
            permission: { deleteByRolesIds: dbDeleteByRolesIds },
            role: { getUsersCount: dbGetUsersCount },
          },
          config: { superAdminCode: SUPER_ADMIN_CODE },
        },
      };

      const deletedRoles = await roleService.deleteByIds(rolesIds);

      expect(dbCount).toHaveBeenNthCalledWith(1, { roles: [rolesIds[0]] });
      expect(dbCount).toHaveBeenNthCalledWith(2, { roles: [rolesIds[1]] });
      expect(dbCount).toHaveBeenCalledTimes(2);
      expect(dbDelete).toHaveBeenCalledWith({ id_in: rolesIds });
      expect(deletedRoles).toStrictEqual(roles);
    });
    test('Cannot delete super admin role', async () => {
      const dbFind = jest.fn(() => [{ id: '1' }]);
      const dbFindOne = jest.fn(() => ({ id: '1', code: SUPER_ADMIN_CODE }));
      const badRequest = jest.fn(() => {});

      global.strapi = {
        query: () => ({ find: dbFind, findOne: dbFindOne }),
        admin: { config: { superAdminCode: SUPER_ADMIN_CODE } },
        errors: { badRequest },
      };

      try {
        await roleService.deleteByIds([1]);
      } catch (e) {
        // nothing
      }

      expect(badRequest).toHaveBeenCalledWith('ValidationError', {
        ids: ['You cannot delete the super admin role'],
      });
    });
  });

  describe('Count roles', () => {
    test('Count roles without params', async () => {
      const count = jest.fn(() => Promise.resolve(2));
      global.strapi = {
        query: () => ({ count }),
      };

      const amount = await roleService.count();

      expect(amount).toBe(2);
      expect(count).toHaveBeenCalledWith({});
    });

    test('Count roles with params', async () => {
      const count = jest.fn(() => Promise.resolve(2));
      global.strapi = {
        query: () => ({ count }),
      };

      const params = { foo: 'bar' };
      const amount = await roleService.count(params);

      expect(amount).toBe(2);
      expect(count).toHaveBeenCalledWith(params);
    });
  });

  describe('createRolesIfNoneExist', () => {
    test("Don't create roles if one already exist", async () => {
      const count = jest.fn(() => Promise.resolve(1));
      const create = jest.fn();
      global.strapi = {
        query: () => ({ count, create }),
      };
      await roleService.createRolesIfNoneExist();

      expect(create).toHaveBeenCalledTimes(0);
    });
    test('Create 3 roles if none exist', async () => {
      const actions = [
        {
          actionId: 'action-1',
          subjects: ['country'],
          section: 'contentTypes',
        },
      ];
      const permissions = [
        {
          action: 'action-1',
          subject: 'country',
          fields: ['name'],
          conditions: [],
        },
      ];

      const defaultPermissions = [
        {
          action: 'plugins::upload.read',
          conditions: ['admin::is-creator'],
          fields: null,
          subject: null,
        },
        {
          action: 'plugins::upload.assets.create',
          conditions: [],
          fields: null,
          subject: null,
        },
        {
          action: 'plugins::upload.assets.update',
          conditions: ['admin::is-creator'],
          fields: null,
          subject: null,
        },
        {
          action: 'plugins::upload.assets.download',
          conditions: [],
          fields: null,
          subject: null,
        },
        {
          action: 'plugins::upload.assets.copy-link',
          conditions: [],
          fields: null,
          subject: null,
        },
      ];

      const count = jest.fn(() => Promise.resolve(0));
      let id = 1;
      const create = jest.fn(role => ({ ...role, id: id++ }));
      const getAll = jest.fn(() => actions);
      const assign = jest.fn();
      const assignARoleToAll = jest.fn();
      const getPermissionsWithNestedFields = jest.fn(() => [...permissions]); // cloned, otherwise it is modified inside createRolesIfNoneExist()

      global.strapi = {
        query: () => ({ count, create }),
        admin: {
          services: {
            permission: { actionProvider: { getAll }, assign },
            'content-type': { getPermissionsWithNestedFields },
            user: { assignARoleToAll },
          },
        },
      };
      await roleService.createRolesIfNoneExist();

      expect(create).toHaveBeenCalledTimes(3);
      expect(create).toHaveBeenNthCalledWith(1, {
        name: 'Super Admin',
        code: 'strapi-super-admin',
        description: 'Super Admins can access and manage all features and settings.',
      });
      expect(assignARoleToAll).toHaveBeenCalledWith(1);
      expect(create).toHaveBeenNthCalledWith(2, {
        name: 'Editor',
        code: 'strapi-editor',
        description: 'Editors can manage and publish contents including those of other users.',
      });
      expect(create).toHaveBeenNthCalledWith(3, {
        name: 'Author',
        code: 'strapi-author',
        description: 'Authors can manage and publish the content they created.',
      });
      expect(getPermissionsWithNestedFields).toHaveBeenCalledWith(actions, {
        restrictedSubjects: ['plugins::users-permissions.user'],
      });
      expect(assign).toHaveBeenCalledTimes(2);
      expect(assign).toHaveBeenNthCalledWith(1, 2, [
        ...permissions,
        ...defaultPermissions.map(d => ({
          ...d,
          conditions: [],
        })),
      ]);

      expect(assign).toHaveBeenNthCalledWith(2, 3, [
        { ...permissions[0], conditions: ['admin::is-creator'] },
        ...defaultPermissions,
      ]);
    });
  });

  describe('displayWarningIfNoSuperAdmin', () => {
    test('superAdmin role exists & a user is superAdmin', async () => {
      const findOne = jest.fn(() => ({ id: 1 }));
      const count = jest.fn(() => Promise.resolve(1));
      const exists = jest.fn(() => Promise.resolve(true));
      const warn = jest.fn();

      global.strapi = {
        query: () => ({ findOne, count }),
        admin: { services: { user: { exists } } },
        log: { warn },
      };

      await roleService.displayWarningIfNoSuperAdmin();

      expect(warn).toHaveBeenCalledTimes(0);
    });
    test("superAdmin role doesn't exist", async () => {
      const findOne = jest.fn(() => undefined);
      const count = jest.fn(() => Promise.resolve(0));
      const exists = jest.fn(() => Promise.resolve(false));
      const warn = jest.fn();

      global.strapi = {
        query: () => ({ findOne, count }),
        admin: { services: { user: { exists } } },
        log: { warn },
      };

      await roleService.displayWarningIfNoSuperAdmin();

      expect(warn).toHaveBeenCalledWith("Your application doesn't have a super admin role.");
    });
    test('superAdmin role exist & no user is superAdmin', async () => {
      const findOne = jest.fn(() => ({ id: 1 }));
      const count = jest.fn(() => Promise.resolve(0));
      const exists = jest.fn(() => Promise.resolve(true));
      const warn = jest.fn();

      global.strapi = {
        query: () => ({ findOne, count }),
        admin: { services: { user: { exists } } },
        log: { warn },
      };

      await roleService.displayWarningIfNoSuperAdmin();

      expect(warn).toHaveBeenCalledWith("Your application doesn't have a super admin user.");
    });
  });
});
<commit_msg>Fix tests<commit_after>'use strict';

const _ = require('lodash');
const roleService = require('../role');
const { SUPER_ADMIN_CODE } = require('../constants');

describe('Role', () => {
  describe('create', () => {
    test('Creates a role', async () => {
      const dbCreate = jest.fn(role => Promise.resolve(role));
      const dbCount = jest.fn(() => Promise.resolve(0));

      global.strapi = {
        query: () => ({ create: dbCreate, count: dbCount }),
      };

      const input = {
        name: 'super_admin',
        description: "Have all permissions. Can't be delete",
        code: 'super-admin',
      };

      const createdRole = await roleService.create(input);

      expect(dbCreate).toHaveBeenCalledWith(input);
      expect(createdRole).toStrictEqual(input);
    });
  });
  describe('findOne', () => {
    test('Finds a role', async () => {
      const role = {
        id: 1,
        name: 'super_admin',
        description: "Have all permissions. Can't be delete",
      };
      const dbFindOne = jest.fn(({ id }) => Promise.resolve(_.find([role], { id })));

      global.strapi = {
        query: () => ({ findOne: dbFindOne }),
      };

      const foundRole = await roleService.findOne({ id: role.id });

      expect(dbFindOne).toHaveBeenCalledWith({ id: role.id }, []);
      expect(foundRole).toStrictEqual(role);
    });
    test('Finds a role with usersCount', async () => {
      const role = {
        id: 1,
        name: 'super_admin',
        description: "Have all permissions. Can't be delete",
        usersCount: 0,
      };
      const dbFindOne = jest.fn(({ id }) =>
        Promise.resolve(_.find([_.omit(role, ['usersCount'])], { id }))
      );
      const dbCount = jest.fn(() => Promise.resolve(0));
      global.strapi = {
        query: () => ({ findOne: dbFindOne, count: dbCount }),
      };

      const foundRole = await roleService.findOneWithUsersCount({ id: role.id });

      expect(dbFindOne).toHaveBeenCalledWith({ id: role.id }, []);
      expect(dbCount).toHaveBeenCalledWith({ roles: [role.id] });
      expect(foundRole).toStrictEqual(role);
    });
  });
  describe('find', () => {
    test('Finds roles', async () => {
      const roles = [
        {
          id: 1,
          name: 'super_admin',
          description: "Have all permissions. Can't be delete",
        },
      ];
      const dbFind = jest.fn(() => Promise.resolve(roles));

      global.strapi = {
        query: () => ({ find: dbFind }),
      };

      const foundRoles = await roleService.find();

      expect(dbFind).toHaveBeenCalledWith({}, []);
      expect(foundRoles).toStrictEqual(roles);
    });
  });
  describe('findAll', () => {
    test('Finds all roles', async () => {
      const roles = [
        {
          id: 1,
          name: 'super_admin',
          description: "Have all permissions. Can't be delete",
          usersCount: 0,
        },
      ];
      const dbFind = jest.fn(() =>
        Promise.resolve(roles.map(role => _.omit(role, ['usersCount'])))
      );
      const dbCount = jest.fn(() => Promise.resolve(0));

      global.strapi = {
        query: () => ({ find: dbFind, count: dbCount }),
      };

      const foundRoles = await roleService.findAllWithUsersCount();

      expect(dbFind).toHaveBeenCalledWith({ _limit: -1 }, []);
      expect(foundRoles).toStrictEqual(roles);
    });
  });
  describe('update', () => {
    test('Updates a role', async () => {
      const role = {
        id: 1,
        name: 'super_admin',
        description: 'AAA',
      };
      const expectedUpdatedRole = {
        id: 1,
        name: 'super_admin_updated',
        description: 'AAA_updated',
      };
      const dbUpdate = jest.fn(() => Promise.resolve(expectedUpdatedRole));
      const dbCount = jest.fn(() => Promise.resolve(0));

      global.strapi = {
        query: () => ({ update: dbUpdate, count: dbCount }),
      };

      const updatedRole = await roleService.update(
        {
          id: role.id,
        },
        {
          name: expectedUpdatedRole.name,
          description: expectedUpdatedRole.description,
        }
      );

      expect(dbUpdate).toHaveBeenCalledWith(
        {
          id: role.id,
        },
        {
          name: expectedUpdatedRole.name,
          description: expectedUpdatedRole.description,
        }
      );
      expect(updatedRole).toStrictEqual(expectedUpdatedRole);
    });
    test('Cannot update code of super admin role', async () => {
      const dbUpdate = jest.fn();
      const dbFind = jest.fn(() => [{ id: '1' }]);
      const dbFindOne = jest.fn(() => ({ id: '1', code: SUPER_ADMIN_CODE }));
      const badRequest = jest.fn(() => {});

      global.strapi = {
        query: () => ({ find: dbFind, findOne: dbFindOne, update: dbUpdate }),
        admin: { config: { superAdminCode: SUPER_ADMIN_CODE } },
        errors: { badRequest },
      };

      await roleService.update({ id: 1 }, { code: 'new_code' });

      expect(dbUpdate).toHaveBeenCalledWith({ id: 1 }, {});
    });
  });
  describe('count', () => {
    test('getUsersCount', async () => {
      const roleId = 1;
      const dbCount = jest.fn(() => Promise.resolve(0));
      global.strapi = {
        query: () => ({ count: dbCount }),
      };

      const usersCount = await roleService.getUsersCount(roleId);

      expect(dbCount).toHaveBeenCalledWith({ roles: [roleId] });
      expect(usersCount).toEqual(0);
    });
  });
  describe('delete', () => {
    test('Delete a role', async () => {
      const role = {
        id: 3,
        name: 'admin',
        description: 'Description',
        users: [],
      };
      const dbCount = jest.fn(() => Promise.resolve(0));
      const dbFindOne = jest.fn(() => ({ id: 1, code: SUPER_ADMIN_CODE }));
      const dbDelete = jest.fn(() => Promise.resolve(role));
      const dbDeleteByRolesIds = jest.fn(() => Promise.resolve());

      global.strapi = {
        query: () => ({ delete: dbDelete, count: dbCount, findOne: dbFindOne }),
        admin: {
          services: {
            permission: { deleteByRolesIds: dbDeleteByRolesIds },
          },
          config: { superAdminCode: SUPER_ADMIN_CODE },
        },
      };

      const deletedRoles = await roleService.deleteByIds([role.id]);

      expect(dbCount).toHaveBeenCalledWith({ roles: [role.id] });
      expect(dbDelete).toHaveBeenCalledWith({ id_in: [role.id] });
      expect(deletedRoles).toStrictEqual([role]);
    });
    test('Delete two roles', async () => {
      const roles = [
        {
          id: 1,
          name: 'admin 1',
          description: 'Description',
          users: [],
        },
        {
          id: 2,
          name: 'admin 2',
          description: 'Description',
          users: [],
        },
      ];
      const dbCount = jest.fn(() => Promise.resolve(0));
      const dbFindOne = jest.fn(() => ({ id: 3, code: SUPER_ADMIN_CODE }));
      const rolesIds = roles.map(r => r.id);
      const dbDelete = jest.fn(() => Promise.resolve(roles));
      const dbGetUsersCount = jest.fn(() => Promise.resolve(0));
      const dbDeleteByRolesIds = jest.fn(() => Promise.resolve());

      global.strapi = {
        query: () => ({ delete: dbDelete, count: dbCount, findOne: dbFindOne }),
        admin: {
          services: {
            permission: { deleteByRolesIds: dbDeleteByRolesIds },
            role: { getUsersCount: dbGetUsersCount },
          },
          config: { superAdminCode: SUPER_ADMIN_CODE },
        },
      };

      const deletedRoles = await roleService.deleteByIds(rolesIds);

      expect(dbCount).toHaveBeenNthCalledWith(1, { roles: [rolesIds[0]] });
      expect(dbCount).toHaveBeenNthCalledWith(2, { roles: [rolesIds[1]] });
      expect(dbCount).toHaveBeenCalledTimes(2);
      expect(dbDelete).toHaveBeenCalledWith({ id_in: rolesIds });
      expect(deletedRoles).toStrictEqual(roles);
    });
    test('Cannot delete super admin role', async () => {
      const dbFind = jest.fn(() => [{ id: '1' }]);
      const dbFindOne = jest.fn(() => ({ id: '1', code: SUPER_ADMIN_CODE }));
      const badRequest = jest.fn(() => {});

      global.strapi = {
        query: () => ({ find: dbFind, findOne: dbFindOne }),
        admin: { config: { superAdminCode: SUPER_ADMIN_CODE } },
        errors: { badRequest },
      };

      try {
        await roleService.deleteByIds([1]);
      } catch (e) {
        // nothing
      }

      expect(badRequest).toHaveBeenCalledWith('ValidationError', {
        ids: ['You cannot delete the super admin role'],
      });
    });
  });

  describe('Count roles', () => {
    test('Count roles without params', async () => {
      const count = jest.fn(() => Promise.resolve(2));
      global.strapi = {
        query: () => ({ count }),
      };

      const amount = await roleService.count();

      expect(amount).toBe(2);
      expect(count).toHaveBeenCalledWith({});
    });

    test('Count roles with params', async () => {
      const count = jest.fn(() => Promise.resolve(2));
      global.strapi = {
        query: () => ({ count }),
      };

      const params = { foo: 'bar' };
      const amount = await roleService.count(params);

      expect(amount).toBe(2);
      expect(count).toHaveBeenCalledWith(params);
    });
  });

  describe('createRolesIfNoneExist', () => {
    test("Don't create roles if one already exist", async () => {
      const count = jest.fn(() => Promise.resolve(1));
      const create = jest.fn();
      global.strapi = {
        query: () => ({ count, create }),
      };
      await roleService.createRolesIfNoneExist();

      expect(create).toHaveBeenCalledTimes(0);
    });
    test('Create 3 roles if none exist', async () => {
      const actions = [
        {
          actionId: 'action-1',
          subjects: ['country'],
          section: 'contentTypes',
        },
      ];
      const permissions = [
        {
          action: 'action-1',
          subject: 'country',
          fields: ['name'],
          conditions: [],
        },
      ];

      const defaultPermissions = [
        {
          action: 'plugins::upload.read',
          conditions: ['admin::is-creator'],
          fields: null,
          subject: null,
        },
        {
          action: 'plugins::upload.assets.create',
          conditions: [],
          fields: null,
          subject: null,
        },
        {
          action: 'plugins::upload.assets.update',
          conditions: ['admin::is-creator'],
          fields: null,
          subject: null,
        },
        {
          action: 'plugins::upload.assets.download',
          conditions: [],
          fields: null,
          subject: null,
        },
        {
          action: 'plugins::upload.assets.copy-link',
          conditions: [],
          fields: null,
          subject: null,
        },
      ];

      const count = jest.fn(() => Promise.resolve(0));
      let id = 1;
      const create = jest.fn(role => ({ ...role, id: id++ }));
      const getAll = jest.fn(() => actions);
      const assign = jest.fn();
      const assignARoleToAll = jest.fn();
      const getPermissionsWithNestedFields = jest.fn(() => [...permissions]); // cloned, otherwise it is modified inside createRolesIfNoneExist()

      global.strapi = {
        query: () => ({ count, create }),
        admin: {
          services: {
            permission: { actionProvider: { getAll }, assign },
            'content-type': { getPermissionsWithNestedFields },
            user: { assignARoleToAll },
          },
        },
      };
      await roleService.createRolesIfNoneExist();

      expect(create).toHaveBeenCalledTimes(3);
      expect(create).toHaveBeenNthCalledWith(1, {
        name: 'Super Admin',
        code: 'strapi-super-admin',
        description: 'Super Admins can access and manage all features and settings.',
      });
      expect(assignARoleToAll).toHaveBeenCalledWith(1);
      expect(create).toHaveBeenNthCalledWith(2, {
        name: 'Editor',
        code: 'strapi-editor',
        description: 'Editors can manage and publish contents including those of other users.',
      });
      expect(create).toHaveBeenNthCalledWith(3, {
        name: 'Author',
        code: 'strapi-author',
        description: 'Authors can manage the content they have created.',
      });
      expect(getPermissionsWithNestedFields).toHaveBeenCalledWith(actions, {
        restrictedSubjects: ['plugins::users-permissions.user'],
      });
      expect(assign).toHaveBeenCalledTimes(2);
      expect(assign).toHaveBeenNthCalledWith(1, 2, [
        ...permissions,
        ...defaultPermissions.map(d => ({
          ...d,
          conditions: [],
        })),
      ]);

      expect(assign).toHaveBeenNthCalledWith(2, 3, [
        { ...permissions[0], conditions: ['admin::is-creator'] },
        ...defaultPermissions,
      ]);
    });
  });

  describe('displayWarningIfNoSuperAdmin', () => {
    test('superAdmin role exists & a user is superAdmin', async () => {
      const findOne = jest.fn(() => ({ id: 1 }));
      const count = jest.fn(() => Promise.resolve(1));
      const exists = jest.fn(() => Promise.resolve(true));
      const warn = jest.fn();

      global.strapi = {
        query: () => ({ findOne, count }),
        admin: { services: { user: { exists } } },
        log: { warn },
      };

      await roleService.displayWarningIfNoSuperAdmin();

      expect(warn).toHaveBeenCalledTimes(0);
    });
    test("superAdmin role doesn't exist", async () => {
      const findOne = jest.fn(() => undefined);
      const count = jest.fn(() => Promise.resolve(0));
      const exists = jest.fn(() => Promise.resolve(false));
      const warn = jest.fn();

      global.strapi = {
        query: () => ({ findOne, count }),
        admin: { services: { user: { exists } } },
        log: { warn },
      };

      await roleService.displayWarningIfNoSuperAdmin();

      expect(warn).toHaveBeenCalledWith("Your application doesn't have a super admin role.");
    });
    test('superAdmin role exist & no user is superAdmin', async () => {
      const findOne = jest.fn(() => ({ id: 1 }));
      const count = jest.fn(() => Promise.resolve(0));
      const exists = jest.fn(() => Promise.resolve(true));
      const warn = jest.fn();

      global.strapi = {
        query: () => ({ findOne, count }),
        admin: { services: { user: { exists } } },
        log: { warn },
      };

      await roleService.displayWarningIfNoSuperAdmin();

      expect(warn).toHaveBeenCalledWith("Your application doesn't have a super admin user.");
    });
  });
});
<|endoftext|>


<commit_before>var chai = require('chai'),
    assert = chai.assert,
    expect = chai.expect,
    request = require('request'),
    async = require('async'),
    http = require('http'),
    zSchema = require('z-schema'),
    util = require('util'),
    _ = require('lodash'),
    Factory = require('../../lib/factory');

var PORT = 12919,
    BASE_URL = 'http://127.0.0.1:' + PORT;

var factoryConfig = {
  port: PORT,
  defaults: {
    branch: 'develop',
    commit: 'HEAD'
  },
  tasks: {
    clone: {
      cmd: 'true',
      timeout: 10000
    },
    checkout: {
      cmd: 'true',
      timeout: 10000
    },
    build: {
      cmd: 'true',
      timeout: 10000
    },
    push: {
      cmd: 'true',
      timeout: 10000
    }
  }
};

var MINIMUM_BUILD_REQUEST = {
  owner: "fake-owner",
  repo: "fake-repo"
};

var GIT_HOOK_PUSH_REQUEST_STR = '{ \
  "ref": "fake-owner/fake-repo/fake-branch", \
  "after": "1234", \
  "repository": { \
    "name": "fake-repo", \
    "owner": { \
      "name": "fake-owner" \
    } \
  }, \
  "deleted": false \
}';

var GIT_HOOK_DELETE_REQUEST_STR = '{ \
  "ref": "fake-owner/fake-repo/fake-branch", \
  "after": "1234", \
  "repository": { \
    "name": "fake-repo", \
    "owner": { \
      "name": "fake-owner" \
    } \
  }, \
  "deleted": true \
}';

var FULL_BUILD_REQUEST = {
  owner: "fake-owner",
  repo: "fake-repo",
  branch: "fake-branch",
  commit: "1234",
  realm: "dev"
};

describe('Image Factory - REST API', function () {

  var factoryInstance;

  beforeEach(function () {
    factoryInstance = new Factory(factoryConfig);
  });

  afterEach(function () {
    factoryInstance.stop();
  });

  describe('POST /job', function () {

    it('should create a new Job and return a status code 202 when POST /job with a valid minimum payload', function (done) {
      request.post(
        BASE_URL + '/job',
        {
          body: JSON.stringify(MINIMUM_BUILD_REQUEST),
          headers: { 'Content-Type': 'application/vnd.sh.melt.cdp.if.job-create.v1+json'}
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(201);
          expect(response.headers).to.include.keys('link');
          expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
          expect(response.headers).to.include.keys('content-type');
          expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job.v1+json');
          var job = JSON.parse(body);
          expect(job.id).to.exist;
          done();
        }
      );
    });

    it('should create a new Job and return a status code 202 when POST /job with a valid full payload', function (done) {
      request.post(
        BASE_URL + '/job',
        {
          body: JSON.stringify(FULL_BUILD_REQUEST),
          headers: { 'Content-Type': 'application/vnd.sh.melt.cdp.if.job-create.v1+json'}
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(201);
          expect(response.headers).to.include.keys('link');
          expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
          expect(response.headers).to.include.keys('content-type');
          expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job.v1+json');
          var job = JSON.parse(body);
          expect(job.id).to.exist;
          done();
        }
      );
    });

    it('should return a status code 400 when POST /job with an invalid payload', function (done) {
      request.post(
        BASE_URL + '/job',
        {
          body: JSON.stringify({ }),
          headers: { 'Content-Type': 'application/vnd.sh.melt.cdp.if.job-create.v1+json'}
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(400);
          expect(body.id).to.not.exist;
          done();
        }
      );
    });

  });

  describe('POST /hooks/github', function () {
    it('should create a new Job and return a status code 201 ' +
        'when POST /hooks/github with a valid payload', function (done) {
      request.post(
        BASE_URL + '/hooks/github',
        {
          body: GIT_HOOK_PUSH_REQUEST_STR,
          headers: {
            'Content-Type': 'application/json',
            'X-GitHub-Event': 'push',
            'X-Hub-Signature': 'sha1=75486e24e49d01aa87cff3951314993ce649cc8a'
          }
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(201);
          expect(response.headers).to.include.keys('link');
          expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
          expect(response.headers).to.include.keys('content-type');
          expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job.v1+json');
          var job = JSON.parse(body);
          expect(job.id).to.exist;
          done();
        }
      );
    });

    it('should return No Content when POST /hooks/github with deleted flag as true', function (done) {
      request.post(
        BASE_URL + '/hooks/github',
        {
          body: GIT_HOOK_DELETE_REQUEST_STR,
          headers: {
            'Content-Type': 'application/json',
            'X-Hub-Signature': 'sha1=b1b6f5e7379d550cfaaf597d90f26d832d999f3f',
            'X-GitHub-Event': 'push'
          }
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(204);
          done();
        }
      );
    });

    it('should return Unauthorized response when invalid signature is passed', function (done) {
      request.post(
              BASE_URL + '/hooks/github',
          {
            body: GIT_HOOK_PUSH_REQUEST_STR,
            headers: {
              'Content-Type': 'application/json',
              'X-Hub-Signature': 'sha1=invalid'
            }
          },
          function (err, response, body) {
            expect(err).to.not.exist;
            expect(response.statusCode).to.equal(401);
            done();
          }
      );
    });

  });

  describe('GET /job/{id}', function () {

    it('should return status code 200 and a valid Job when requesting GET /job/{id} using a valid {id}', function (done) {

      request.post(
        BASE_URL + '/job',
        { json: FULL_BUILD_REQUEST },
        function (err, response, body) {
          get(body.id);
        }
      );

      function get(id) {
        request.get(
          BASE_URL + '/job/' + id,
          function (err, response, body) {
            expect(err).to.not.exist;
            expect(response.statusCode).to.equal(200);
            expect(response.headers).to.include.keys('link');
            expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
            expect(response.headers).to.include.keys('content-type');
            expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job.v1+json');
            var job = JSON.parse(body);
            expect(job.id).to.equal(id);
            done();
          }
        );
      }
    });

    it('should return a status code 404 when requesting GET /job/{id} using an invalid {id}', function (done) {
      request.get(
        BASE_URL + '/job/does-not-exist',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(404);
          done();
        }
      );
    });

    it('should return a Job that validates to a JSON Schema provided by a Link header when requesting GET /job/{id} using a valid {id}', function (done) {

      request.post(
        BASE_URL + '/job',
        { json: FULL_BUILD_REQUEST },
        function (err, response, body) {
          get(body.id);
        }
      );

      function get(id) {
        request.get(
          BASE_URL + '/job/' + id,
          function (err, response, body) {
            expect(err).to.not.exist;

            expect(response.headers).to.include.keys('link');
            expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
            var job = JSON.parse(body);

            request.get(
              BASE_URL + '/_schema/job',
              function (err, response, body) {
                expect(err).to.not.exist;
                var schema = JSON.parse(body);
                var report = zSchema.validate(job, schema, function(err) {
                  expect(err).to.not.exist;
                  done();
                });
              }
            );
          }
        );
      }
    });

  });

  describe('GET /job/{id}/log', function () {

    it('should return status code 200 and a execution log when requesting GET /job/{id}/log using a valid {id}', function (done) {

      request.post(
        BASE_URL + '/job',
        { json: FULL_BUILD_REQUEST },
        function (err, response, body) {
          setTimeout(function() {
            get(body.id);
          }, 1000); // Give some time to start the job to complete.
        }
      );

      function get(id) {
        request.get(
          BASE_URL + '/job/' + id + '/log',
          function (err, response, body) {
            expect(err).to.not.exist;
            expect(response.statusCode).to.equal(200);
            expect(body).to.contain('Command true completed successfuly');
            done();
          }
        );
      }
    });

    it('should return a status code 404 when requesting GET /job/{id}/log using an invalid {id}', function (done) {
      request.get(
        BASE_URL + '/job/does-not-exist/log',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(404);
          done();
        }
      );
    });

  });

  describe('GET /job', function () {

    it('should return a list of known Jobs when requesting GET /job', function (done) {

      async.times(
        5,
        function (n, next) {
          request.post(
            BASE_URL + '/job',
            { json: FULL_BUILD_REQUEST },
            function (err, response, body) {
              next(err);
            }
          );
        },
        function (err, results) {
          request.get(
            BASE_URL + '/job',
            function (err, response, body) {
              expect(err).to.not.exist;
              expect(response.statusCode).to.equal(200);
              expect(response.headers).to.include.keys('link');
              expect(response.headers['link']).to.contain('</_schema/job-list>; rel="describedBy"');
              expect(response.headers).to.include.keys('content-type');
              expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job-list.v1+json');
              var results = JSON.parse(body);
              expect(results).to.have.length(5);
              done();
            }
          );
        }
      );

    });

  });

  describe('GET /_schema', function () {

    it('should return a valid draft-4 schema for GET /_schema/ROOT', function (done) {

      request.get(
        BASE_URL + '/_schema/ROOT',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          var schema = JSON.parse(body);
          expect(schema['$schema']).to.equal('http://json-schema.org/draft-04/schema');
          done();
        }
      );

    });

    it('should return a valid draft-4 schema for GET /_schema/job', function (done) {

      request.get(
        BASE_URL + '/_schema/job',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          var schema = JSON.parse(body);
          expect(schema['$schema']).to.equal('http://json-schema.org/draft-04/schema');
          done();
        }
      );

    });

    it('should return a valid draft-4 schema for GET /_schema/job-list', function (done) {

      request.get(
        BASE_URL + '/_schema/job-list',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          var schema = JSON.parse(body);
          expect(schema['$schema']).to.equal('http://json-schema.org/draft-04/schema');
          done();
        }
      );

    });

    it('should return a valid draft-4 schema for GET /_schema/job-create', function (done) {

      request.get(
        BASE_URL + '/_schema/job-create',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          var schema = JSON.parse(body);
          expect(schema['$schema']).to.equal('http://json-schema.org/draft-04/schema');
          done();
        }
      );

    });

  });

  describe('GET /', function () {

    it('should GET / and return a valid JSON Hyper-Schema Link', function (done) {

      request.get(
        BASE_URL + '/',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          expect(response.headers).to.include.keys('link');
          expect(response.headers['link']).to.contain('</_schema/ROOT>; rel="describedBy"');
          done();
        }
      );

    });

  });

});<commit_msg>Added integration test for queues.<commit_after>var chai = require('chai'),
    assert = chai.assert,
    expect = chai.expect,
    request = require('request'),
    async = require('async'),
    http = require('http'),
    zSchema = require('z-schema'),
    util = require('util'),
    _ = require('lodash'),
    constants = require('../../lib/constants'),
    Factory = require('../../lib/factory');

var PORT = 12919,
    BASE_URL = 'http://127.0.0.1:' + PORT;

var factoryConfig = {
  port: PORT,
  defaults: {
    branch: 'develop',
    commit: 'HEAD'
  },
  tasks: {
    clone: {
      cmd: 'true',
      timeout: 10000
    },
    checkout: {
      cmd: 'true',
      timeout: 10000
    },
    build: {
      cmd: 'true',
      timeout: 10000
    },
    push: {
      cmd: 'true',
      timeout: 10000
    },
    concurrentJobs: 1
  }
};

var MINIMUM_BUILD_REQUEST = {
  owner: "fake-owner",
  repo: "fake-repo"
};

var GIT_HOOK_PUSH_REQUEST_STR = '{ \
  "ref": "fake-owner/fake-repo/fake-branch", \
  "after": "1234", \
  "repository": { \
    "name": "fake-repo", \
    "owner": { \
      "name": "fake-owner" \
    } \
  }, \
  "deleted": false \
}';

var GIT_HOOK_DELETE_REQUEST_STR = '{ \
  "ref": "fake-owner/fake-repo/fake-branch", \
  "after": "1234", \
  "repository": { \
    "name": "fake-repo", \
    "owner": { \
      "name": "fake-owner" \
    } \
  }, \
  "deleted": true \
}';

var FULL_BUILD_REQUEST = {
  owner: "fake-owner",
  repo: "fake-repo",
  branch: "fake-branch",
  commit: "1234",
  realm: "dev"
};

describe('Image Factory - REST API', function () {

  var factoryInstance;

  beforeEach(function () {
    factoryInstance = new Factory(factoryConfig);
  });

  afterEach(function () {
    factoryInstance.stop();
  });

  describe('POST /job', function () {

    it('should create a new Job and return a status code 202 when POST /job with a valid minimum payload', function (done) {
      request.post(
        BASE_URL + '/job',
        {
          body: JSON.stringify(MINIMUM_BUILD_REQUEST),
          headers: { 'Content-Type': 'application/vnd.sh.melt.cdp.if.job-create.v1+json'}
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(201);
          expect(response.headers).to.include.keys('link');
          expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
          expect(response.headers).to.include.keys('content-type');
          expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job.v1+json');
          var job = JSON.parse(body);
          expect(job.id).to.exist;
          done();
        }
      );
    });

    it('should create a new Job and return a status code 202 when POST /job with a valid full payload', function (done) {
      request.post(
        BASE_URL + '/job',
        {
          body: JSON.stringify(FULL_BUILD_REQUEST),
          headers: { 'Content-Type': 'application/vnd.sh.melt.cdp.if.job-create.v1+json'}
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(201);
          expect(response.headers).to.include.keys('link');
          expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
          expect(response.headers).to.include.keys('content-type');
          expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job.v1+json');
          var job = JSON.parse(body);
          expect(job.id).to.exist;
          done();
        }
      );
    });

    it('should return a status code 400 when POST /job with an invalid payload', function (done) {
      request.post(
        BASE_URL + '/job',
        {
          body: JSON.stringify({ }),
          headers: { 'Content-Type': 'application/vnd.sh.melt.cdp.if.job-create.v1+json'}
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(400);
          expect(body.id).to.not.exist;
          done();
        }
      );
    });

    it('should create a new Job that is pending in queue while a previous job is building when POST /job with one concurrent job', function (done) {

      // Creates the first job
      var firstJob;
      request.post(
        BASE_URL + '/job',
        {
          body: JSON.stringify({
            owner: "fake-owner-0",
            repo: "fake-repo-0"
          }),
          headers: { 'Content-Type': 'application/vnd.sh.melt.cdp.if.job-create.v1+json'}
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(201);
          expect(response.headers).to.include.keys('link');
          expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
          expect(response.headers).to.include.keys('content-type');
          expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job.v1+json');
          var job = JSON.parse(body);
          expect(job.id).to.exist;
          firstJob = job.id;
        }
      );

      // Creates the second job
      request.post(
        BASE_URL + '/job',
        {
          body: JSON.stringify({
            owner: "fake-owner-1",
            repo: "fake-repo-1"
          }),
          headers: { 'Content-Type': 'application/vnd.sh.melt.cdp.if.job-create.v1+json'}
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          var job = JSON.parse(body);
          expect(job.id).to.exist;

          // The number of jobs waiting in queue should be 1
          expect(factoryInstance.jobQueue.length()).to.equal(1);

          // The number of jobs being worked on should be 1, and should be the first job
          expect(factoryInstance.jobQueue.workersList().length).to.equal(1);
          expect(factoryInstance.jobQueue.workersList()[0].data.id).to.equal(firstJob);
          done();
        }
      );
    });

  });

  describe('POST /hooks/github', function () {
    it('should create a new Job and return a status code 201 ' +
        'when POST /hooks/github with a valid payload', function (done) {
      request.post(
        BASE_URL + '/hooks/github',
        {
          body: GIT_HOOK_PUSH_REQUEST_STR,
          headers: {
            'Content-Type': 'application/json',
            'X-GitHub-Event': 'push',
            'X-Hub-Signature': 'sha1=75486e24e49d01aa87cff3951314993ce649cc8a'
          }
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(201);
          expect(response.headers).to.include.keys('link');
          expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
          expect(response.headers).to.include.keys('content-type');
          expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job.v1+json');
          var job = JSON.parse(body);
          expect(job.id).to.exist;
          done();
        }
      );
    });

    it('should return No Content when POST /hooks/github with deleted flag as true', function (done) {
      request.post(
        BASE_URL + '/hooks/github',
        {
          body: GIT_HOOK_DELETE_REQUEST_STR,
          headers: {
            'Content-Type': 'application/json',
            'X-Hub-Signature': 'sha1=b1b6f5e7379d550cfaaf597d90f26d832d999f3f',
            'X-GitHub-Event': 'push'
          }
        },
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(204);
          done();
        }
      );
    });

    it('should return Unauthorized response when invalid signature is passed', function (done) {
      request.post(
              BASE_URL + '/hooks/github',
          {
            body: GIT_HOOK_PUSH_REQUEST_STR,
            headers: {
              'Content-Type': 'application/json',
              'X-Hub-Signature': 'sha1=invalid'
            }
          },
          function (err, response, body) {
            expect(err).to.not.exist;
            expect(response.statusCode).to.equal(401);
            done();
          }
      );
    });

  });

  describe('GET /job/{id}', function () {

    it('should return status code 200 and a valid Job when requesting GET /job/{id} using a valid {id}', function (done) {

      request.post(
        BASE_URL + '/job',
        { json: FULL_BUILD_REQUEST },
        function (err, response, body) {
          get(body.id);
        }
      );

      function get(id) {
        request.get(
          BASE_URL + '/job/' + id,
          function (err, response, body) {
            expect(err).to.not.exist;
            expect(response.statusCode).to.equal(200);
            expect(response.headers).to.include.keys('link');
            expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
            expect(response.headers).to.include.keys('content-type');
            expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job.v1+json');
            var job = JSON.parse(body);
            expect(job.id).to.equal(id);
            done();
          }
        );
      }
    });

    it('should return a status code 404 when requesting GET /job/{id} using an invalid {id}', function (done) {
      request.get(
        BASE_URL + '/job/does-not-exist',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(404);
          done();
        }
      );
    });

    it('should return a Job that validates to a JSON Schema provided by a Link header when requesting GET /job/{id} using a valid {id}', function (done) {

      request.post(
        BASE_URL + '/job',
        { json: FULL_BUILD_REQUEST },
        function (err, response, body) {
          get(body.id);
        }
      );

      function get(id) {
        request.get(
          BASE_URL + '/job/' + id,
          function (err, response, body) {
            expect(err).to.not.exist;

            expect(response.headers).to.include.keys('link');
            expect(response.headers['link']).to.contain('</_schema/job>; rel="describedBy"');
            var job = JSON.parse(body);

            request.get(
              BASE_URL + '/_schema/job',
              function (err, response, body) {
                expect(err).to.not.exist;
                var schema = JSON.parse(body);
                var report = zSchema.validate(job, schema, function(err) {
                  expect(err).to.not.exist;
                  done();
                });
              }
            );
          }
        );
      }
    });

  });

  describe('GET /job/{id}/log', function () {

    it('should return status code 200 and a execution log when requesting GET /job/{id}/log using a valid {id}', function (done) {

      request.post(
        BASE_URL + '/job',
        { json: FULL_BUILD_REQUEST },
        function (err, response, body) {
          setTimeout(function() {
            get(body.id);
          }, 1000); // Give some time to start the job to complete.
        }
      );

      function get(id) {
        request.get(
          BASE_URL + '/job/' + id + '/log',
          function (err, response, body) {
            expect(err).to.not.exist;
            expect(response.statusCode).to.equal(200);
            expect(body).to.contain('Command true completed successfuly');
            done();
          }
        );
      }
    });

    it('should return a status code 404 when requesting GET /job/{id}/log using an invalid {id}', function (done) {
      request.get(
        BASE_URL + '/job/does-not-exist/log',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(404);
          done();
        }
      );
    });

  });

  describe('GET /job', function () {

    it('should return a list of known Jobs when requesting GET /job', function (done) {

      async.times(
        5,
        function (n, next) {
          request.post(
            BASE_URL + '/job',
            { json: FULL_BUILD_REQUEST },
            function (err, response, body) {
              next(err);
            }
          );
        },
        function (err, results) {
          request.get(
            BASE_URL + '/job',
            function (err, response, body) {
              expect(err).to.not.exist;
              expect(response.statusCode).to.equal(200);
              expect(response.headers).to.include.keys('link');
              expect(response.headers['link']).to.contain('</_schema/job-list>; rel="describedBy"');
              expect(response.headers).to.include.keys('content-type');
              expect(response.headers['content-type']).to.contain('application/vnd.sh.melt.cdp.if.job-list.v1+json');
              var results = JSON.parse(body);
              expect(results).to.have.length(5);
              done();
            }
          );
        }
      );

    });

  });

  describe('GET /_schema', function () {

    it('should return a valid draft-4 schema for GET /_schema/ROOT', function (done) {

      request.get(
        BASE_URL + '/_schema/ROOT',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          var schema = JSON.parse(body);
          expect(schema['$schema']).to.equal('http://json-schema.org/draft-04/schema');
          done();
        }
      );

    });

    it('should return a valid draft-4 schema for GET /_schema/job', function (done) {

      request.get(
        BASE_URL + '/_schema/job',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          var schema = JSON.parse(body);
          expect(schema['$schema']).to.equal('http://json-schema.org/draft-04/schema');
          done();
        }
      );

    });

    it('should return a valid draft-4 schema for GET /_schema/job-list', function (done) {

      request.get(
        BASE_URL + '/_schema/job-list',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          var schema = JSON.parse(body);
          expect(schema['$schema']).to.equal('http://json-schema.org/draft-04/schema');
          done();
        }
      );

    });

    it('should return a valid draft-4 schema for GET /_schema/job-create', function (done) {

      request.get(
        BASE_URL + '/_schema/job-create',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          var schema = JSON.parse(body);
          expect(schema['$schema']).to.equal('http://json-schema.org/draft-04/schema');
          done();
        }
      );

    });

  });

  describe('GET /', function () {

    it('should GET / and return a valid JSON Hyper-Schema Link', function (done) {

      request.get(
        BASE_URL + '/',
        function (err, response, body) {
          expect(err).to.not.exist;
          expect(response.statusCode).to.equal(200);
          expect(response.headers).to.include.keys('link');
          expect(response.headers['link']).to.contain('</_schema/ROOT>; rel="describedBy"');
          done();
        }
      );

    });

  });

});<|endoftext|>