mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-05-22 11:55:24 +00:00
test: migration from Python to JavaScript
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import config from '../lib/config'
|
||||
|
||||
class ManageSieve {
|
||||
constructor(login, authname, password) {
|
||||
const Telnet = require('telnet-client')
|
||||
|
||||
this.login = login
|
||||
this.authname = authname
|
||||
this.password = password
|
||||
this.params = {
|
||||
host: config.sieve_server,
|
||||
port: config.sieve_port,
|
||||
shellPrompt: /\bOK "/,
|
||||
timeout: 1500
|
||||
}
|
||||
this.connection = new Telnet()
|
||||
this.sasl = []
|
||||
this.ready = false
|
||||
}
|
||||
|
||||
async connect() {
|
||||
let response, parsedResponse
|
||||
|
||||
if (!this.ready) {
|
||||
await this.connection.connect(this.params)
|
||||
response = await this.connection.send('CAPABILITY', { waitfor: /\b(OK|NO) "/ })
|
||||
// console.debug(`ManageSieve.connect => ${response}`)
|
||||
parsedResponse = this.parseResponse(response)
|
||||
if (!parsedResponse['OK']) {
|
||||
throw new Error(`Connection failed: ${parsedResponse['NO']}`)
|
||||
}
|
||||
this.ready = true
|
||||
if (parsedResponse['SASL']) {
|
||||
this.sasl = parsedResponse['SASL'].split(/ /)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async authenticate() {
|
||||
let buff, base64, response, parsedResponse
|
||||
|
||||
await this.connect()
|
||||
|
||||
buff = Buffer.from(`${this.login}\0${this.authname}\0${this.password}`)
|
||||
base64 = buff.toString('base64')
|
||||
response = await this.connection.send(`AUTHENTICATE "PLAIN" {${base64.length}+}\n${base64}`, { waitfor: /\b(OK|NO) "/ })
|
||||
// console.debug(`ManageSieve.authenticate => ${response}`)
|
||||
parsedResponse = this.parseResponse(response)
|
||||
if (!parsedResponse['OK']) {
|
||||
throw new Error(`Authentication failed: ${parsedResponse['NO']}`)
|
||||
}
|
||||
}
|
||||
|
||||
async listScripts() {
|
||||
let response, parsedResponse
|
||||
|
||||
await this.connect()
|
||||
|
||||
response = await this.connection.send(`LISTSCRIPTS`, { waitfor: /\b(OK|NO) "/ })
|
||||
parsedResponse = this.parseResponse(response)
|
||||
// console.debug(`ManageSieve.listScripts => ${JSON.stringify(parsedResponse, undefined, 2)}`)
|
||||
if (!parsedResponse['OK']) {
|
||||
throw new Error(`List scripts failed: ${parsedResponse['NO']}`)
|
||||
}
|
||||
return parsedResponse
|
||||
}
|
||||
|
||||
async getScript(scriptname) {
|
||||
let response, parsedResponse, script = null
|
||||
|
||||
await this.connect()
|
||||
|
||||
response = await this.connection.send(`GETSCRIPT "${scriptname}"`, { waitfor: /\b(OK|NO) "/ })
|
||||
// console.debug(`ManageSieve.getScript(${scriptname}) => |${response}|`)
|
||||
const lengthMatch = response.match(/{([0-9]+)}\r?\n/)
|
||||
if (lengthMatch) {
|
||||
const scriptLength = lengthMatch[1]
|
||||
script = response.substr(lengthMatch[0].length, scriptLength)
|
||||
}
|
||||
else
|
||||
throw new Error(`Can't find length of Sieve script`)
|
||||
|
||||
return script
|
||||
}
|
||||
|
||||
parseResponse(str) {
|
||||
const re = new RegExp(/[^\s"]+|"([^"]*)"/gi)
|
||||
let parsed = {}
|
||||
for (let line of (str.split(/\r?\n/))) {
|
||||
if (line.length) {
|
||||
let rematch, key, value, i = 0
|
||||
while ((rematch = re.exec(line))) {
|
||||
value = rematch[1] ? rematch[1] : rematch[0]
|
||||
if (key && i > 0)
|
||||
parsed[key] = value
|
||||
else
|
||||
key = value
|
||||
i++
|
||||
}
|
||||
if (key && i == 1) {
|
||||
parsed[key] = null
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.debug(`ManageSieve.parseResponse => ${JSON.stringify(parsed, undefined, 2)}`)
|
||||
return parsed
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ManageSieve
|
||||
+18
-16
@@ -28,17 +28,21 @@ class Preferences {
|
||||
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]))
|
||||
if (response.status == 200) {
|
||||
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('; ')
|
||||
}
|
||||
this.cookie = authCookies.join('; ')
|
||||
else
|
||||
throw new Error(`Can't authenticate with username ${this.username} (HTTP status code ${response.status})`)
|
||||
}
|
||||
return this.cookie
|
||||
}
|
||||
@@ -137,15 +141,13 @@ class Preferences {
|
||||
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]
|
||||
}
|
||||
let obj = this.preferences
|
||||
for (let path of paths) {
|
||||
if (typeof obj[path] == 'undefined')
|
||||
obj[path] = {}
|
||||
obj = obj[path]
|
||||
}
|
||||
|
||||
obj[preference] = value
|
||||
}
|
||||
|
||||
|
||||
+45
-41
@@ -5,9 +5,10 @@ import {
|
||||
|
||||
davRequest,
|
||||
deleteObject,
|
||||
formatProps,
|
||||
getBasicAuthHeaders,
|
||||
getDAVAttribute,
|
||||
propfind,
|
||||
syncCollection,
|
||||
|
||||
calendarMultiGet,
|
||||
createCalendarObject,
|
||||
@@ -15,8 +16,6 @@ import {
|
||||
|
||||
createVCard
|
||||
} from 'tsdav'
|
||||
import { formatProps, getDAVAttribute } from 'tsdav/dist/util/requestHelpers';
|
||||
import { makeCollection } from 'tsdav/dist/collection';
|
||||
import convert from 'xml-js'
|
||||
import { fetch } from 'cross-fetch'
|
||||
import config from './config'
|
||||
@@ -132,10 +131,14 @@ class WebDAV {
|
||||
}
|
||||
|
||||
makeCollection(resource) {
|
||||
return makeCollection({
|
||||
return davRequest({
|
||||
url: this.serverUrl + resource,
|
||||
headers: this.headers
|
||||
});
|
||||
init: {
|
||||
method: 'MKCOL',
|
||||
headers: this.headers,
|
||||
namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
propfindWebdav(resource, properties, namespace = DAVNamespace.DAV, headers = {}) {
|
||||
@@ -325,6 +328,42 @@ class WebDAV {
|
||||
})
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc6578#section-3.2
|
||||
syncQuery(resource, token = '', properties) {
|
||||
const formattedProperties = properties.map((p) => {
|
||||
return { [`${DAVNamespaceShorthandMap[DAVNamespace.DAV]}:${p}`]: '' }
|
||||
});
|
||||
let xmlBody = convert.js2xml(
|
||||
{
|
||||
'sync-collection': {
|
||||
_attributes: getDAVAttribute([DAVNamespace.DAV]),
|
||||
'sync-level': 1,
|
||||
'sync-token': token,
|
||||
prop: formattedProperties
|
||||
}
|
||||
},
|
||||
{
|
||||
compact: true,
|
||||
spaces: 2,
|
||||
elementNameFn: (name) => {
|
||||
// add namespace to all keys without namespace
|
||||
if (!/^.+:.+/.test(name)) {
|
||||
return `${DAVNamespaceShorthandMap[DAVNamespace.DAV]}:${name}`
|
||||
}
|
||||
return name
|
||||
}
|
||||
}
|
||||
)
|
||||
return fetch(this.serverUrl + resource, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml; charset="utf-8"',
|
||||
...this.headers
|
||||
},
|
||||
method: 'REPORT',
|
||||
body: xmlBody
|
||||
})
|
||||
}
|
||||
|
||||
// CalDAV operations
|
||||
|
||||
makeCalendar(resource) {
|
||||
@@ -426,41 +465,6 @@ class WebDAV {
|
||||
})
|
||||
}
|
||||
|
||||
syncQuery(resource, token = '', properties) {
|
||||
const formattedProperties = properties.map((p) => {
|
||||
return { [`${DAVNamespaceShorthandMap[DAVNamespace.DAV]}:${p}`]: '' }
|
||||
});
|
||||
let xmlBody = convert.js2xml(
|
||||
{
|
||||
'sync-collection': {
|
||||
_attributes: getDAVAttribute([DAVNamespace.DAV]),
|
||||
'sync-level': 1,
|
||||
'sync-token': token,
|
||||
prop: formattedProperties
|
||||
}
|
||||
},
|
||||
{
|
||||
compact: true,
|
||||
spaces: 2,
|
||||
elementNameFn: (name) => {
|
||||
// add namespace to all keys without namespace
|
||||
if (!/^.+:.+/.test(name)) {
|
||||
return `${DAVNamespaceShorthandMap[DAVNamespace.DAV]}:${name}`
|
||||
}
|
||||
return name
|
||||
}
|
||||
}
|
||||
)
|
||||
return fetch(this.serverUrl + resource, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml; charset="utf-8"',
|
||||
...this.headers
|
||||
},
|
||||
method: 'REPORT',
|
||||
body: xmlBody
|
||||
})
|
||||
}
|
||||
|
||||
propfindCaldav(resource, properties, depth = 0) {
|
||||
return this.propfindWebdav(resource, properties, DAVNamespace.CALDAV, { depth: new String(depth) })
|
||||
}
|
||||
|
||||
+4
-3
@@ -6,14 +6,15 @@
|
||||
"test": "jasmine"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.15.8",
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/preset-env": "^7.16.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"cookie": "^0.4.1",
|
||||
"cross-fetch": "^3.1.4",
|
||||
"esm": "^3.2.25",
|
||||
"ical.js": "^1.4.0",
|
||||
"jasmine": "^3.10.0",
|
||||
"tsdav": "^1.1.1"
|
||||
"telnet-client": "^1.4.10",
|
||||
"tsdav": "^1.1.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,14 +82,6 @@ describe('PreventInvitations', function() {
|
||||
|
||||
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)
|
||||
@@ -114,6 +106,16 @@ describe('PreventInvitations', function() {
|
||||
icsList = []
|
||||
})
|
||||
|
||||
beforeEach(async function() {
|
||||
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)
|
||||
})
|
||||
|
||||
afterAll(async function() {
|
||||
await prefs.set('PreventInvitationsWhitelist', {})
|
||||
await prefs.set('PreventInvitations', 0)
|
||||
|
||||
@@ -5,9 +5,10 @@ import TestUtility from '../lib/utilities'
|
||||
import {
|
||||
DAVNamespace,
|
||||
DAVNamespaceShorthandMap,
|
||||
davRequest
|
||||
davRequest,
|
||||
formatProps,
|
||||
getDAVAttribute
|
||||
} from 'tsdav'
|
||||
import { formatProps, getDAVAttribute } from 'tsdav/dist/util/requestHelpers';
|
||||
|
||||
const cards = {
|
||||
'new.vcf': `BEGIN:VCARD
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
import config from '../lib/config'
|
||||
import WebDAV from '../lib/WebDAV'
|
||||
import TestUtility from '../lib/utilities'
|
||||
import Preferences from '../lib/Preferences'
|
||||
import ManageSieve from '../lib/ManageSieve'
|
||||
|
||||
let prefs, webdav, utility, manageSieve, user
|
||||
|
||||
describe('Sieve', function() {
|
||||
|
||||
async function _getSogoSieveScript() {
|
||||
const scripts = await manageSieve.listScripts()
|
||||
|
||||
expect(Object.keys(scripts))
|
||||
.withContext(`sogo sieve script has been created`)
|
||||
.toContain('sogo')
|
||||
expect(scripts['sogo'])
|
||||
.withContext(`sogo sieve script is active`)
|
||||
.toMatch(/ACTIVE/i)
|
||||
|
||||
const script = await manageSieve.getScript('sogo')
|
||||
|
||||
return script
|
||||
}
|
||||
|
||||
async function _killFilters() {
|
||||
// kill existing filters
|
||||
await prefs.setOrCreate('SOGoSieveFilters', [], ['defaults'])
|
||||
// vacation filters
|
||||
await prefs.setOrCreate('autoReplyText', '', ['defaults', 'Vacation'])
|
||||
await prefs.setOrCreate('customSubjectEnabled', 0, ['defaults', 'Vacation'])
|
||||
await prefs.setOrCreate('customSubject', '', ['defaults', 'Vacation'])
|
||||
await prefs.setOrCreate('autoReplyEmailAddresses', [], ['defaults', 'Vacation'])
|
||||
await prefs.setOrCreate('daysBetweenResponse', 7, ['defaults', 'Vacation'])
|
||||
await prefs.setOrCreate('ignoreLists', 0, ['defaults', 'Vacation'])
|
||||
await prefs.setOrCreate('startDate', 0, ['defaults', 'Vacation'])
|
||||
await prefs.setOrCreate('endDate', 0, ['defaults', 'Vacation'])
|
||||
await prefs.setOrCreate('enabled', 0, ['defaults', 'Vacation'])
|
||||
// forwarding filters
|
||||
await prefs.setOrCreate('forwardAddress', [], ['defaults', 'Forward'])
|
||||
await prefs.setOrCreate('keepCopy', 0, ['defaults', 'Forward'])
|
||||
}
|
||||
|
||||
beforeAll(async function() {
|
||||
prefs = new Preferences(config.username, config.password)
|
||||
webdav = new WebDAV(config.username, config.password)
|
||||
utility = new TestUtility(webdav)
|
||||
manageSieve = new ManageSieve(config.username, config.username, config.password)
|
||||
user = await utility.fetchUserInfo(config.username)
|
||||
|
||||
await manageSieve.authenticate()
|
||||
})
|
||||
|
||||
beforeEach(async function() {
|
||||
await _killFilters()
|
||||
})
|
||||
|
||||
afterAll(async function() {
|
||||
await _killFilters()
|
||||
await prefs.save()
|
||||
})
|
||||
|
||||
it('enable simple vacation script', async function() {
|
||||
const vacationMsg = 'vacation test'
|
||||
const daysInterval = 5
|
||||
const mailaddr = user.email
|
||||
const sieveSimpleVacation = `require ["vacation"];\r\nvacation :days ${daysInterval} :addresses ["${mailaddr}"] text:\r\n${vacationMsg}\r\n.\r\n;\r\n`
|
||||
let vacation
|
||||
|
||||
vacation = await prefs.get('Vacation')
|
||||
vacation.enabled = 1
|
||||
await prefs.setNoSave('autoReplyText', vacationMsg)
|
||||
await prefs.setNoSave('daysBetweenResponse', daysInterval)
|
||||
await prefs.setNoSave('autoReplyEmailAddresses', [user.email])
|
||||
await prefs.save()
|
||||
|
||||
const createdScript = await _getSogoSieveScript()
|
||||
expect(createdScript)
|
||||
.withContext(`sogo Sieve script`)
|
||||
.toBe(sieveSimpleVacation)
|
||||
})
|
||||
|
||||
it('enable vacation script - ignore lists', async function() {
|
||||
const vacationMsg = 'vacation test - ignore list'
|
||||
const daysInterval = 3
|
||||
const mailaddr = user.email
|
||||
const sieveVacationIgnoreLists = `require ["vacation"];\r\nif allof ( not exists ["list-help", "list-unsubscribe", "list-subscribe", "list-owner", "list-post", "list-archive", "list-id", "Mailing-List"], not header :comparator "i;ascii-casemap" :is "Precedence" ["list", "bulk", "junk"], not header :comparator "i;ascii-casemap" :matches "To" "Multiple recipients of*" ) { vacation :days ${daysInterval} :addresses ["${mailaddr}"] text:\r\n${vacationMsg}\r\n.\r\n;\r\n}\r\n`
|
||||
let vacation
|
||||
|
||||
vacation = await prefs.get('Vacation')
|
||||
vacation.enabled = 1
|
||||
await prefs.setNoSave('autoReplyText', vacationMsg)
|
||||
await prefs.setNoSave('daysBetweenResponse', daysInterval)
|
||||
await prefs.setNoSave('autoReplyEmailAddresses', [user.email])
|
||||
await prefs.setNoSave('ignoreLists', 1)
|
||||
await prefs.save()
|
||||
|
||||
const createdScript = await _getSogoSieveScript()
|
||||
expect(createdScript)
|
||||
.withContext(`sogo Sieve script`)
|
||||
.toBe(sieveVacationIgnoreLists)
|
||||
})
|
||||
|
||||
it('enable simple forwarding', async function() {
|
||||
const redirectMailaddr = 'nonexistent@inverse.com'
|
||||
const sieveSimpleForward = `redirect "${redirectMailaddr}";\r\n`
|
||||
let forward
|
||||
|
||||
// Enabling Forward now is an 'enabled' setting in the subdict Forward
|
||||
// We need to get that subdict first -- next save/set will also save this
|
||||
forward = await prefs.get('Forward')
|
||||
forward.enabled = 1
|
||||
await prefs.set('forwardAddress', [redirectMailaddr])
|
||||
|
||||
const createdScript = await _getSogoSieveScript()
|
||||
expect(createdScript)
|
||||
.withContext(`sogo Sieve script`)
|
||||
.toBe(sieveSimpleForward)
|
||||
})
|
||||
|
||||
it('enable email forwarding - keep a copy', async function() {
|
||||
const redirectMailaddr = 'nonexistent@inverse.com'
|
||||
const sieveForwardKeep = `redirect "${redirectMailaddr}";\r\nkeep;\r\n`
|
||||
let forward
|
||||
|
||||
// Enabling Forward now is an 'enabled' setting in the subdict Forward
|
||||
// We need to get that subdict first -- next save/set will also save this
|
||||
forward = await prefs.get('Forward')
|
||||
forward.enabled = 1
|
||||
await prefs.setNoSave('forwardAddress', [redirectMailaddr])
|
||||
await prefs.setNoSave('keepCopy', 1)
|
||||
await prefs.save()
|
||||
|
||||
const createdScript = await _getSogoSieveScript()
|
||||
expect(createdScript)
|
||||
.withContext(`sogo Sieve script`)
|
||||
.toBe(sieveForwardKeep)
|
||||
})
|
||||
|
||||
it('add simple sieve filter', async function() {
|
||||
const folderName = 'Sent'
|
||||
const subject = 'add simple sieve filter'
|
||||
const sieveFilter = `require ["fileinto"];\r\nif anyof (header :contains "subject" "${subject}") {\r\n fileinto "${folderName}";\r\n}\r\n`
|
||||
|
||||
await prefs.set('SOGoSieveFilters', [{
|
||||
active: true,
|
||||
actions: [{
|
||||
method: 'fileinto',
|
||||
argument: 'Sent'
|
||||
}],
|
||||
rules: [{
|
||||
operator: 'contains',
|
||||
field: 'subject',
|
||||
value: subject
|
||||
}],
|
||||
match: 'any',
|
||||
name: folderName
|
||||
}])
|
||||
|
||||
const createdScript = await _getSogoSieveScript()
|
||||
expect(createdScript)
|
||||
.withContext(`sogo Sieve script`)
|
||||
.toBe(sieveFilter)
|
||||
})
|
||||
})
|
||||
@@ -12,7 +12,7 @@ describe('webdav sync', function() {
|
||||
await webdav_su.deleteObject(resource)
|
||||
})
|
||||
|
||||
it("webdav sync", async function() {
|
||||
it('webdav sync', async function() {
|
||||
const nsShort = DAVNamespaceShorthandMap[DAVNamespace.DAV].toUpperCase()
|
||||
let response, xml, token
|
||||
|
||||
@@ -23,7 +23,9 @@ describe('webdav sync', function() {
|
||||
|
||||
response = await webdav.makeCalendar(resource)
|
||||
expect(response.length).toBe(1)
|
||||
expect(response[0].status).toBe(201)
|
||||
expect(response[0].status)
|
||||
.withContext(`HTTP status code when creating a Calendar`)
|
||||
.toBe(201)
|
||||
|
||||
// test queries:
|
||||
// empty collection:
|
||||
@@ -36,15 +38,23 @@ describe('webdav sync', function() {
|
||||
response = await webdav.syncQuery(resource, null, [ 'getetag' ])
|
||||
xml = await response.text();
|
||||
({ [`${nsShort}:multistatus`]: { [`${nsShort}:sync-token`]: { _text: token } } } = convert.xml2js(xml, {compact: true, nativeType: true}))
|
||||
expect(response.status).toBe(207)
|
||||
expect(token).toBeGreaterThanOrEqual(0)
|
||||
expect(response.status)
|
||||
.withContext(`HTTP status code when performing sync-query without a token`)
|
||||
.toBe(207)
|
||||
expect(token)
|
||||
.withContext(`Sync query returns valid token`)
|
||||
.toBeGreaterThanOrEqual(0)
|
||||
|
||||
// we make sure that any token is accepted when the collection is
|
||||
// empty, but that the returned token differs
|
||||
response = await webdav.syncQuery(resource, '1234', [ 'getetag' ])
|
||||
xml = await response.text();
|
||||
({ [`${nsShort}:multistatus`]: { [`${nsShort}:sync-token`]: { _text: token } } } = convert.xml2js(xml, {compact: true, nativeType: true}))
|
||||
expect(response.status).toBe(207)
|
||||
expect(token).toBeGreaterThanOrEqual(0)
|
||||
expect(response.status)
|
||||
.withContext(`HTTP status code when performing sync-query with a token`)
|
||||
.toBe(207)
|
||||
expect(token)
|
||||
.withContext(`Sync query returns valid token`)
|
||||
.toBeGreaterThanOrEqual(0)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user