Skip to content

Commit

Permalink
test: migration from Python to JavaScript
Browse files Browse the repository at this point in the history
  • Loading branch information
cgx committed Sep 2, 2021
1 parent 6ae787c commit 2feee5f
Show file tree
Hide file tree
Showing 5 changed files with 402 additions and 68 deletions.
168 changes: 168 additions & 0 deletions Tests/lib/Preferences.js
@@ -0,0 +1,168 @@
import cookie from 'cookie'
import { fetch } from 'cross-fetch'
import config from './config'

/**
* NOTE
*
* For this class to be used, make sure XSRF validation is disabled on the server.
* In sogo.conf, you must have:
*
* SOGoXSRFValidationEnabled = NO;
*/

class Preferences {
constructor(un, pw) {
this.username = un
this.password = pw
this.serverUrl = `http://${config.hostname}:${config.port}`
this.cookie = null
this.preferences = null
}

async getAuthCookie() {
if (!this.cookie) {
const resource = `/SOGo/connect`
const response = await fetch(this.serverUrl + resource, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({userName: this.username, password: this.password})
})
const values = response.headers.get('set-cookie').split(/, /)
let authCookies = []
for (let v of values) {
let c = cookie.parse(v)
for (let authCookie of ['0xHIGHFLYxSOGo', 'XSRF-TOKEN']) {
if (Object.keys(c).includes('0xHIGHFLYxSOGo')) {
authCookies.push(cookie.serialize(authCookie, c[authCookie]))
}
}
}
this.cookie = authCookies.join('; ')
}
return this.cookie
}

async getDefaults() {
const resource = `/SOGo/so/${this.username}/jsonDefaults`
const authCookie = await this.getAuthCookie()
const response = await fetch(this.serverUrl + resource, {
method: 'GET',
headers: {
Cookie: authCookie,
...this.headers
}
})
if (response.status == 200) {
const defaults = await response.json()
return defaults
}
else
throw new Error(`Can't fetch defaults of user ${this.username}: ${response.status}`)
}

async getSettings() {
const resource = `/SOGo/so/${this.username}/jsonSettings`
const authCookie = await this.getAuthCookie()
const response = await fetch(this.serverUrl + resource, {
method: 'GET',
headers: {
Cookie: authCookie,
...this.headers
}
})
if (response.status == 200) {
const settings = await response.json()
return settings
}
else
throw new Error(`Can't fetch settings of user ${this.username}: ${response.status}`)
}

async loadPreferences() {
const defaults = await this.getDefaults()
const settings = await this.getSettings()

this.preferences = { defaults, settings }
}

findKey(obj, key) {
if (Object.keys(obj).includes(key)) {
return obj
}
for (let k of Object.keys(obj)) {
if (typeof obj[k] == 'object') {
let o = this.findKey(obj[k], key)
if (o !== null)
return o
}
}
return null
}

async get(preference, withCache = true) {
if (!withCache || !this.preferences)
await this.loadPreferences()

if (!preference)
return this.preferences // return everything

const obj = this.findKey(this.preferences, preference)
if (obj)
return obj[preference]
else
return null
}

async setNoSave(preference, value) {
if (!this.preferences)
await this.loadPreferences()

const obj = this.findKey(this.preferences, preference)
if (obj == null)
throw new Error(`Can't find key ${preference} in preferences`)

if (typeof value == 'undefined')
delete obj[preference]
else
obj[preference] = value
}

async set(preference, value) {
await this.setNoSave(preference, value)
return await this.save()
}

async setOrCreate(preference, value, paths = ['defaults']) {
if (!this.preferences)
await this.loadPreferences()

let obj = this.findKey(this.preferences, preference)
if (obj == null) {
obj = this.preferences
for (let path of paths) {
if (typeof obj[path] == 'undefined')
obj[path] = {}
obj = obj[path]
}
}
obj[preference] = value
}

async save() {
const resource = `/SOGo/so/${this.username}/Preferences/save`
const authCookie = await this.getAuthCookie()
const response = await fetch(this.serverUrl + resource, {
method: 'POST',
headers: {
Cookie: authCookie,
'Content-Type': 'application/json',
...this.headers
},
body: JSON.stringify(this.preferences)
})
return response
}
}

export default Preferences
29 changes: 29 additions & 0 deletions Tests/lib/utilities.js
Expand Up @@ -170,6 +170,35 @@ class TestUtility {
return true
}

createDateTimeProperty(propertyName, dateObject = new Date()) {
let property = new ICAL.Property(propertyName)
property.setParameter('tzid', 'America/Toronto')
property.setValue(ICAL.Time.fromJSDate(dateObject))

return property
}

createCalendar(summary = 'test event', uid = 'test', transp = 'OPAQUE') {
const vcalendar = new ICAL.Component('vcalendar')
const vevent = new ICAL.Component('vevent')
const now = new Date()
const later = new Date(now.getTime() + 1000*60*60) // event lasts one hour

vcalendar.addSubcomponent(vevent)
vevent.addPropertyWithValue('uid', uid)
vevent.addPropertyWithValue('summary', summary)
vevent.addPropertyWithValue('transp', transp)
vevent.addProperty(this.createDateTimeProperty('dtstart', now))
vevent.addProperty(this.createDateTimeProperty('dtend', later))
vevent.addProperty(this.createDateTimeProperty('dtstamp', now))
vevent.addProperty(this.createDateTimeProperty('last-modified', now))
vevent.addProperty(this.createDateTimeProperty('created', now))
vevent.addPropertyWithValue('class', 'PUBLIC')
vevent.addPropertyWithValue('sequence', '0')

return vcalendar
}

}

export default TestUtility
166 changes: 166 additions & 0 deletions Tests/spec/CalDAVPreventInvitationsSpec.js
@@ -0,0 +1,166 @@
import config from '../lib/config'
import WebDAV from '../lib/WebDAV'
import TestUtility from '../lib/utilities'
import Preferences from '../lib/Preferences'
import ICAL from 'ical.js'

// preventInvitationsTest
// CalDAVSchedulingTest

let prefs
let webdav, webdav_su, webdavAttendee1, webdavAttendee1Delegate
let utility, user, attendee1, attendee1Delegate
let userCalendar, attendee1Calendar, attendee1DelegateCalendar
let icsName, icsList, vcalendar

describe('PreventInvitationsWhitelist user setting', function() {

const _getEvent = async function(client, calendarName, filename, expectedCode = 200) {
const [{ status, headers, raw }] = await client.getEvent(calendarName, filename)
expect(status).toBe(expectedCode)
if (status <= 300)
return new ICAL.Component(ICAL.parse(raw))
return false
}

const _putEvent = async function(client, calendarName, filename, event, expectedCode = 201) {
const response = await client.createCalendarObject(calendarName, filename, event.toString())
expect(response.status)
.withContext(`Create event ${calendarName}${filename}`)
.toBe(expectedCode)
return response
}

const _addAttendee = async function(expectedCode = 204) {
let vevent, organizer, attendee

// add attendee after event creation
icsName = 'test-add-attendee.ics'
icsList.push(icsName)

await webdav.deleteObject(userCalendar + icsName)
await webdavAttendee1.deleteObject(attendee1Calendar + icsName)

// 1. create an event in the organiser's calendar
vcalendar = utility.createCalendar('Test add attendee', 'test-add-attendee')
vevent = vcalendar.getFirstSubcomponent('vevent')
organizer = new ICAL.Property('organizer')
organizer.setParameter('cn', user.displayname)
organizer.setValue(user.email)
vevent.addProperty(organizer)
await _putEvent(webdav, userCalendar, icsName, vcalendar)

// 2. add an attendee
vcalendar.addPropertyWithValue('method', 'REQUEST')
attendee = new ICAL.Property('attendee')
attendee.setParameter('cn', attendee1.displayname)
attendee.setParameter('rsvp', 'TRUE')
attendee.setParameter('partstat', 'NEEDS-ACTION')
attendee.setValue(attendee1.email)
vevent.addProperty(attendee)
await _putEvent(webdav, userCalendar, icsName, vcalendar, expectedCode)

// NOTE: vcalendar and icsName are global for _verifyEvent
}

const _verifyEvent = async function(expectedCode = 200) {
// 1. verify that the attendee has the event
const vcalendarAttendee = await _getEvent(webdavAttendee1, attendee1Calendar, icsName, expectedCode)

// 2. make sure the received event match the original one
if (vcalendarAttendee) {
const veventAttendee = vcalendarAttendee.getFirstSubcomponent('vevent')
const vevent = vcalendar.getFirstSubcomponent('vevent')
const uidAttendee = veventAttendee.getFirstProperty('uid').getFirstValue()
const uid = vevent.getFirstProperty('uid').getFirstValue()
expect(uidAttendee)
.toEqual(uid)
}
}

beforeAll(async function() {
prefs = new Preferences(config.attendee1_username, config.attendee1_password)
const calendarPrefs = prefs.get('Calendar')
if (!calendarPrefs.PreventInvitationsWhitelist)
calendarPrefs.PreventInvitationsWhitelist = {}
await prefs.set('PreventInvitationsWhitelist', {})
if (!calendarPrefs.PreventInvitations)
calendarPrefs.PreventInvitations = 0
await prefs.set('PreventInvitations', 0)

webdav = new WebDAV(config.username, config.password)
webdav_su = new WebDAV(config.superuser, config.superuser_password)
webdavAttendee1 = new WebDAV(config.attendee1, config.attendee1_password)
webdavAttendee1Delegate = new WebDAV(config.attendee1_delegate_username, config.attendee1_delegate_password)

utility = new TestUtility(webdav)
user = await utility.fetchUserInfo(config.username)
attendee1 = await utility.fetchUserInfo(config.attendee1)
attendee1Delegate = await utility.fetchUserInfo(config.attendee1_delegate)

userCalendar = `/SOGo/dav/${config.username}/Calendar/personal/`
attendee1Calendar = `/SOGo/dav/${config.attendee1}/Calendar/personal/`
attendee1DelegateCalendar = `/SOGo/dav/${config.attendee1_delegate}/Calendar/personal/`

// fetch non existing event to let sogo create the calendars in the db
await _getEvent(webdav, userCalendar, 'nonexistent', 404)
await _getEvent(webdavAttendee1, attendee1Calendar, 'nonexistent', 404)
await _getEvent(webdavAttendee1Delegate, attendee1DelegateCalendar, 'nonexistent', 404)

// list of ics used by the test.
// afterAll will loop over this and wipe them in all users' calendar
icsList = []
})

afterAll(async function() {
await prefs.set('PreventInvitationsWhitelist', {})
await prefs.set('PreventInvitations', 0)
// delete all created events from all users' calendar
for (const ics of icsList) {
await webdav_su.deleteObject(userCalendar + ics)
await webdav_su.deleteObject(attendee1Calendar + ics)
await webdav_su.deleteObject(attendee1DelegateCalendar + ics)
}
})

it(`Set/get the PreventInvitation pref`, async function() {
// First accept the invitation
await prefs.set('PreventInvitations', 0)
const settings = await prefs.getSettings()
const { Calendar: { PreventInvitations } = {} } = settings
expect(PreventInvitations)
.withContext(`Don't prevent invitations`)
.toBe(0)
await _addAttendee()
await _verifyEvent()
})

it(`Set PreventInvitation and don't accept the Invitation`, async function() {
// Second, enable PreventInviation and refuse it
await prefs.set('PreventInvitations', 1)
const settings = await prefs.getSettings()
const { Calendar: { PreventInvitations } = {} } = settings
expect(PreventInvitations)
.withContext(`Prevent invitations is enabled`)
.toBe(1)
await _addAttendee(409)
await _verifyEvent(404)
})

it(`Set PreventInvitation add to WhiteList and accept the Invitation`, async function() {
// First, add the Organiser to the Attendee's whitelist
await prefs.set('PreventInvitations', 1)
await prefs.set('PreventInvitationsWhitelist', config.white_listed_attendee)
const settings = await prefs.getSettings()
const { Calendar: { PreventInvitations, PreventInvitationsWhitelist } = {} } = settings
expect(PreventInvitations)
.withContext(`Prevent invitations is enabled`)
.toBe(1)
expect(PreventInvitationsWhitelist)
.withContext(`Prevent invitations is enabled, one user is whitelisted`)
.toEqual(config.white_listed_attendee)
// Second, try again to invite, it should work
await _addAttendee()
await _verifyEvent()
})
})

0 comments on commit 2feee5f

Please sign in to comment.