mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 11:39:17 +00:00
909 lines
28 KiB
Go
909 lines
28 KiB
Go
package databag
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/websocket"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const testTimeout = 5
|
|
|
|
type testCondition struct {
|
|
check func(*TestApp) bool
|
|
channel chan bool
|
|
}
|
|
|
|
type testTopic struct {
|
|
topic Topic
|
|
tags map[string]*Tag
|
|
}
|
|
|
|
type testChannel struct {
|
|
channel Channel
|
|
topics map[string]*testTopic
|
|
}
|
|
|
|
//TestContactData holds contact data for a test
|
|
type TestContactData struct {
|
|
token string
|
|
card Card
|
|
profileRevision int64
|
|
viewRevision int64
|
|
articleRevision int64
|
|
channelRevision int64
|
|
cardDetailRevision int64
|
|
cardProfileRevision int64
|
|
articles map[string]*Article
|
|
channels map[string]*testChannel
|
|
offsync bool
|
|
}
|
|
|
|
func (c *TestContactData) updateContact() (err error) {
|
|
|
|
if c.cardDetailRevision != c.card.Data.DetailRevision {
|
|
if err = c.updateContactCardDetail(); err != nil {
|
|
return
|
|
}
|
|
c.cardDetailRevision = c.card.Data.DetailRevision
|
|
}
|
|
|
|
if c.cardProfileRevision != c.card.Data.ProfileRevision {
|
|
if err = c.updateContactCardProfile(); err != nil {
|
|
return
|
|
}
|
|
c.cardProfileRevision = c.card.Data.ProfileRevision
|
|
}
|
|
|
|
// sync rest only if connected
|
|
if c.card.Data.CardDetail.Status != APPCardConnected {
|
|
return
|
|
}
|
|
|
|
if c.profileRevision != c.card.Data.NotifiedProfile {
|
|
if err = c.updateContactProfile(); err != nil {
|
|
return
|
|
}
|
|
c.profileRevision = c.card.Data.NotifiedProfile
|
|
}
|
|
|
|
if c.viewRevision != c.card.Data.NotifiedView {
|
|
if err = c.updateContactArticle(); err != nil {
|
|
return
|
|
} else if err = c.updateContactChannels(); err != nil {
|
|
return
|
|
}
|
|
c.articleRevision = c.card.Data.NotifiedArticle
|
|
c.channelRevision = c.card.Data.NotifiedChannel
|
|
c.viewRevision = c.card.Data.NotifiedView
|
|
}
|
|
|
|
if c.articleRevision != c.card.Data.NotifiedArticle {
|
|
if err = c.updateContactArticle(); err != nil {
|
|
return
|
|
}
|
|
c.articleRevision = c.card.Data.NotifiedArticle
|
|
}
|
|
|
|
if c.channelRevision != c.card.Data.NotifiedChannel {
|
|
if err = c.updateContactChannels(); err != nil {
|
|
return
|
|
}
|
|
c.channelRevision = c.card.Data.NotifiedChannel
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (c *TestContactData) updateContactProfile() (err error) {
|
|
var msg DataMessage
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
params := &TestAPIParams{query: "/profile/message", tokenType: APPTokenContact, token: token}
|
|
response := &TestAPIResponse{data: &msg}
|
|
if err = TestAPIRequest(GetProfileMessage, params, response); err != nil {
|
|
return
|
|
}
|
|
params = &TestAPIParams{restType: "PUT", query: "/contact/cards/{cardID}/profile", tokenType: APPTokenAgent, token: c.token,
|
|
path: map[string]string{"cardID": c.card.ID}, body: &msg}
|
|
response = &TestAPIResponse{}
|
|
if err = TestAPIRequest(SetCardProfile, params, response); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *TestContactData) updateContactArticle() (err error) {
|
|
var articles []Article
|
|
if c.articleRevision == 0 || c.viewRevision != c.card.Data.NotifiedView {
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
params := &TestAPIParams{query: "/articles", tokenType: APPTokenContact, token: token}
|
|
response := &TestAPIResponse{data: &articles}
|
|
if err = TestAPIRequest(GetArticles, params, response); err != nil {
|
|
return
|
|
}
|
|
c.articles = make(map[string]*Article)
|
|
} else {
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
viewRevision := strconv.FormatInt(c.viewRevision, 10)
|
|
articleRevision := strconv.FormatInt(c.articleRevision, 10)
|
|
params := &TestAPIParams{query: "/articles?articleRevision=" + articleRevision + "&viewRevision=" + viewRevision,
|
|
tokenType: APPTokenContact, token: token}
|
|
response := &TestAPIResponse{data: &articles}
|
|
if err = TestAPIRequest(GetArticles, params, response); err != nil {
|
|
return
|
|
}
|
|
}
|
|
for _, article := range articles {
|
|
if article.Data == nil {
|
|
delete(c.articles, article.ID)
|
|
} else {
|
|
c.articles[article.ID] = &article
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *TestContactData) updateContactChannels() (err error) {
|
|
var channels []Channel
|
|
if c.channelRevision == 0 || c.viewRevision != c.card.Data.NotifiedView {
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
params := &TestAPIParams{query: "/channels", tokenType: APPTokenContact, token: token}
|
|
response := &TestAPIResponse{data: &channels}
|
|
if err = TestAPIRequest(GetChannels, params, response); err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
viewRevision := strconv.FormatInt(c.viewRevision, 10)
|
|
channelRevision := strconv.FormatInt(c.channelRevision, 10)
|
|
params := &TestAPIParams{query: "/channels?channelRevision=" + channelRevision + "&viewRevision=" + viewRevision,
|
|
tokenType: APPTokenContact, token: token}
|
|
response := &TestAPIResponse{data: &channels}
|
|
if err = TestAPIRequest(GetChannels, params, response); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, channel := range channels {
|
|
if channel.Data == nil {
|
|
delete(c.channels, channel.ID)
|
|
} else {
|
|
tc, set := c.channels[channel.ID]
|
|
if set {
|
|
if channel.Revision != tc.channel.Revision {
|
|
if err = c.updateContactChannel(tc, &channel); err != nil {
|
|
return
|
|
}
|
|
tc.channel.Revision = channel.Revision
|
|
}
|
|
} else {
|
|
tc := &testChannel{channel: Channel{ID: channel.ID, Data: &ChannelData{}}}
|
|
c.channels[channel.ID] = tc
|
|
if err = c.updateContactChannel(tc, &channel); err != nil {
|
|
return
|
|
}
|
|
tc.channel.Revision = channel.Revision
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *TestContactData) updateContactChannel(storeChannel *testChannel, channel *Channel) (err error) {
|
|
if storeChannel.channel.Revision != channel.Revision {
|
|
if storeChannel.channel.Data.TopicRevision != channel.Data.TopicRevision {
|
|
if err = c.updateContactChannelTopics(storeChannel); err != nil {
|
|
return
|
|
}
|
|
storeChannel.channel.Data.TopicRevision = channel.Data.TopicRevision
|
|
}
|
|
if channel.Data.ChannelDetail != nil {
|
|
storeChannel.channel.Data.ChannelDetail = channel.Data.ChannelDetail
|
|
storeChannel.channel.Data.DetailRevision = channel.Data.DetailRevision
|
|
} else if storeChannel.channel.Data.DetailRevision != channel.Data.DetailRevision {
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
params := &TestAPIParams{query: "/channel/{channelID}", path: map[string]string{"channelID": channel.ID},
|
|
tokenType: APPTokenContact, token: token}
|
|
detail := &ChannelDetail{}
|
|
response := &TestAPIResponse{data: &detail}
|
|
if err = TestAPIRequest(GetChannelDetail, params, response); err != nil {
|
|
return
|
|
}
|
|
if channel.Data == nil {
|
|
err = errors.New("channel removed during update")
|
|
return
|
|
}
|
|
storeChannel.channel.Data.ChannelDetail = detail
|
|
storeChannel.channel.Data.DetailRevision = channel.Data.DetailRevision
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *TestContactData) updateContactChannelTopics(storeChannel *testChannel) (err error) {
|
|
var topics []Topic
|
|
if storeChannel.channel.Revision == 0 {
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics",
|
|
path: map[string]string{"channelID": storeChannel.channel.ID}, tokenType: APPTokenContact, token: token}
|
|
response := &TestAPIResponse{data: &topics}
|
|
if err = TestAPIRequest(GetChannelTopics, params, response); err != nil {
|
|
return
|
|
}
|
|
storeChannel.topics = make(map[string]*testTopic)
|
|
} else {
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
revision := strconv.FormatInt(storeChannel.channel.Revision, 10)
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics?revision=" + revision,
|
|
path: map[string]string{"channelID": storeChannel.channel.ID}, tokenType: APPTokenContact, token: token}
|
|
response := &TestAPIResponse{data: &topics}
|
|
if err = TestAPIRequest(GetChannelTopics, params, response); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, topic := range topics {
|
|
if topic.Data == nil {
|
|
delete(storeChannel.topics, topic.ID)
|
|
} else {
|
|
storeTopic, set := storeChannel.topics[topic.ID]
|
|
if set {
|
|
if topic.Revision != storeTopic.topic.Revision {
|
|
if err = c.updateContactChannelTopic(storeChannel, storeTopic, &topic); err != nil {
|
|
return
|
|
}
|
|
storeTopic.topic.Revision = topic.Revision
|
|
}
|
|
} else {
|
|
storeTopic := &testTopic{topic: Topic{ID: topic.ID, Data: &TopicData{}}}
|
|
storeChannel.topics[topic.ID] = storeTopic
|
|
if err = c.updateContactChannelTopic(storeChannel, storeTopic, &topic); err != nil {
|
|
return
|
|
}
|
|
storeTopic.topic.Revision = topic.Revision
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *TestContactData) updateContactChannelTopic(storeChannel *testChannel, storeTopic *testTopic, topic *Topic) (err error) {
|
|
if storeTopic.topic.Revision != topic.Revision {
|
|
if storeTopic.topic.Data.TagRevision != topic.Data.TagRevision {
|
|
if err = c.updateContactChannelTopicTags(storeChannel, storeTopic); err != nil {
|
|
return
|
|
}
|
|
storeTopic.topic.Data.TagRevision = topic.Data.TagRevision
|
|
}
|
|
if topic.Data.TopicDetail != nil {
|
|
storeTopic.topic.Data.TopicDetail = topic.Data.TopicDetail
|
|
storeTopic.topic.Data.DetailRevision = topic.Data.DetailRevision
|
|
} else if storeTopic.topic.Data.DetailRevision != topic.Data.DetailRevision {
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics/{topicID}",
|
|
path: map[string]string{"channelID": storeChannel.channel.ID, "topicID": topic.ID},
|
|
tokenType: APPTokenContact, token: token}
|
|
topic := Topic{}
|
|
response := &TestAPIResponse{data: &topic}
|
|
if err = TestAPIRequest(GetChannelTopic, params, response); err != nil {
|
|
return
|
|
}
|
|
if topic.Data == nil {
|
|
err = errors.New("topic removed during update")
|
|
return
|
|
}
|
|
storeTopic.topic.Data.TopicDetail = topic.Data.TopicDetail
|
|
storeTopic.topic.Data.DetailRevision = topic.Data.DetailRevision
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *TestContactData) updateContactChannelTopicTags(storeChannel *testChannel, storeTopic *testTopic) (err error) {
|
|
var tags []Tag
|
|
if storeTopic.topic.Revision == 0 {
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics/{topicID}/tags",
|
|
path: map[string]string{"channelID": storeChannel.channel.ID, "topicID": storeTopic.topic.ID},
|
|
tokenType: APPTokenContact, token: token}
|
|
response := &TestAPIResponse{data: &tags}
|
|
if err = TestAPIRequest(GetChannelTopicTags, params, response); err != nil {
|
|
return
|
|
}
|
|
storeTopic.tags = make(map[string]*Tag)
|
|
} else {
|
|
revision := strconv.FormatInt(storeTopic.topic.Revision, 10)
|
|
token := c.card.Data.CardProfile.GUID + "." + c.card.Data.CardDetail.Token
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics/{topicID}/tags?revision=" + revision,
|
|
path: map[string]string{"channelID": storeChannel.channel.ID, "topicID": storeTopic.topic.ID},
|
|
tokenType: APPTokenContact, token: token}
|
|
response := &TestAPIResponse{data: &tags}
|
|
if err = TestAPIRequest(GetChannelTopicTags, params, response); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, tag := range tags {
|
|
if tag.Data == nil {
|
|
delete(storeTopic.tags, tag.ID)
|
|
} else {
|
|
storeTopic.tags[tag.ID] = &tag
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *TestContactData) updateContactCardDetail() (err error) {
|
|
var cardDetail CardDetail
|
|
params := &TestAPIParams{query: "/contact/cards/{cardID}/detail", tokenType: APPTokenAgent, token: c.token,
|
|
path: map[string]string{"cardID": c.card.ID}}
|
|
response := &TestAPIResponse{data: &cardDetail}
|
|
if err = TestAPIRequest(GetCardDetail, params, response); err != nil {
|
|
return
|
|
}
|
|
c.card.Data.CardDetail = &cardDetail
|
|
return
|
|
}
|
|
|
|
func (c *TestContactData) updateContactCardProfile() (err error) {
|
|
var cardProfile CardProfile
|
|
params := &TestAPIParams{query: "/contact/cards/{cardID}/profile", tokenType: APPTokenAgent, token: c.token,
|
|
path: map[string]string{"cardID": c.card.ID}}
|
|
response := &TestAPIResponse{data: &cardProfile}
|
|
if err = TestAPIRequest(GetCardProfile, params, response); err != nil {
|
|
return
|
|
}
|
|
c.card.Data.CardProfile = &cardProfile
|
|
return
|
|
}
|
|
|
|
//NewTestApp allocate a new TestApp structure
|
|
func NewTestApp() *TestApp {
|
|
return &TestApp{
|
|
groups: make(map[string]*Group),
|
|
articles: make(map[string]*Article),
|
|
channels: make(map[string]*testChannel),
|
|
contacts: make(map[string]*TestContactData),
|
|
}
|
|
}
|
|
|
|
//TestApp holds an instance of test app use case
|
|
type TestApp struct {
|
|
name string
|
|
revision Revision
|
|
profile Profile
|
|
groups map[string]*Group
|
|
articles map[string]*Article
|
|
channels map[string]*testChannel
|
|
contacts map[string]*TestContactData
|
|
|
|
token string
|
|
|
|
mutex sync.Mutex
|
|
condition *testCondition
|
|
}
|
|
|
|
func (a *TestApp) updateProfile() (err error) {
|
|
params := &TestAPIParams{query: "/profile", tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &a.profile}
|
|
err = TestAPIRequest(GetProfile, params, response)
|
|
return
|
|
}
|
|
|
|
func (a *TestApp) updateGroups() (err error) {
|
|
var groups []Group
|
|
if a.revision.Group == 0 {
|
|
params := &TestAPIParams{query: "/groups", tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &groups}
|
|
if err = TestAPIRequest(GetGroups, params, response); err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
revision := strconv.FormatInt(a.revision.Group, 10)
|
|
params := &TestAPIParams{query: "/groups?revision=" + revision, tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &groups}
|
|
if err = TestAPIRequest(GetGroups, params, response); err != nil {
|
|
return
|
|
}
|
|
}
|
|
for _, group := range groups {
|
|
if group.Data == nil {
|
|
delete(a.groups, group.ID)
|
|
} else {
|
|
a.groups[group.ID] = &group
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *TestApp) updateArticles() (err error) {
|
|
var articles []Article
|
|
if a.revision.Article == 0 {
|
|
params := &TestAPIParams{query: "/articles", tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &articles}
|
|
if err = TestAPIRequest(GetArticles, params, response); err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
revision := strconv.FormatInt(a.revision.Article, 10)
|
|
params := &TestAPIParams{query: "/articles?articleRevision=" + revision, tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &articles}
|
|
if err = TestAPIRequest(GetArticles, params, response); err != nil {
|
|
return
|
|
}
|
|
}
|
|
for _, article := range articles {
|
|
if article.Data == nil {
|
|
delete(a.articles, article.ID)
|
|
} else {
|
|
a.articles[article.ID] = &article
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *TestApp) updateChannels() (err error) {
|
|
var channels []Channel
|
|
if a.revision.Channel == 0 {
|
|
params := &TestAPIParams{query: "/channels", tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &channels}
|
|
if err = TestAPIRequest(GetChannels, params, response); err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
revision := strconv.FormatInt(a.revision.Channel, 10)
|
|
params := &TestAPIParams{query: "/channels?channelRevision=" + revision, tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &channels}
|
|
if err = TestAPIRequest(GetChannels, params, response); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, channel := range channels {
|
|
if channel.Data == nil {
|
|
delete(a.channels, channel.ID)
|
|
} else {
|
|
storeChannel, set := a.channels[channel.ID]
|
|
if set {
|
|
if channel.Revision != storeChannel.channel.Revision {
|
|
if err = a.updateChannel(storeChannel, &channel); err != nil {
|
|
return
|
|
}
|
|
storeChannel.channel.Revision = channel.Revision
|
|
}
|
|
} else {
|
|
storeChannel := &testChannel{channel: Channel{ID: channel.ID, Data: &ChannelData{}}}
|
|
a.channels[channel.ID] = storeChannel
|
|
if err = a.updateChannel(storeChannel, &channel); err != nil {
|
|
return
|
|
}
|
|
storeChannel.channel.Revision = channel.Revision
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *TestApp) updateChannel(storeChannel *testChannel, channel *Channel) (err error) {
|
|
if storeChannel.channel.Revision != channel.Revision {
|
|
if storeChannel.channel.Data.TopicRevision != channel.Data.TopicRevision {
|
|
if err = a.updateChannelTopics(storeChannel); err != nil {
|
|
return
|
|
}
|
|
storeChannel.channel.Data.TopicRevision = channel.Data.TopicRevision
|
|
}
|
|
if channel.Data.ChannelDetail != nil {
|
|
storeChannel.channel.Data.ChannelDetail = channel.Data.ChannelDetail
|
|
storeChannel.channel.Data.DetailRevision = channel.Data.DetailRevision
|
|
} else if storeChannel.channel.Data.DetailRevision != channel.Data.DetailRevision {
|
|
params := &TestAPIParams{query: "/channel/{channelID}", path: map[string]string{"channelID": channel.ID},
|
|
tokenType: APPTokenAgent, token: a.token}
|
|
detail := &ChannelDetail{}
|
|
response := &TestAPIResponse{data: &detail}
|
|
if err = TestAPIRequest(GetChannelDetail, params, response); err != nil {
|
|
return
|
|
}
|
|
if channel.Data == nil {
|
|
err = errors.New("channel removed during update")
|
|
return
|
|
}
|
|
storeChannel.channel.Data.ChannelDetail = detail
|
|
storeChannel.channel.Data.DetailRevision = channel.Data.DetailRevision
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *TestApp) updateChannelTopics(storeChannel *testChannel) (err error) {
|
|
var topics []Topic
|
|
if storeChannel.channel.Revision == 0 {
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics",
|
|
path: map[string]string{"channelID": storeChannel.channel.ID}, tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &topics}
|
|
if err = TestAPIRequest(GetChannelTopics, params, response); err != nil {
|
|
return
|
|
}
|
|
storeChannel.topics = make(map[string]*testTopic)
|
|
} else {
|
|
revision := strconv.FormatInt(storeChannel.channel.Revision, 10)
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics?revision=" + revision,
|
|
path: map[string]string{"channelID": storeChannel.channel.ID}, tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &topics}
|
|
if err = TestAPIRequest(GetChannelTopics, params, response); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, topic := range topics {
|
|
if topic.Data == nil {
|
|
delete(storeChannel.topics, topic.ID)
|
|
} else {
|
|
storeTopic, set := storeChannel.topics[topic.ID]
|
|
if set {
|
|
if topic.Revision != storeTopic.topic.Revision {
|
|
if err = a.updateChannelTopic(storeChannel, storeTopic, &topic); err != nil {
|
|
return
|
|
}
|
|
storeTopic.topic.Revision = topic.Revision
|
|
}
|
|
} else {
|
|
storeTopic := &testTopic{topic: Topic{ID: topic.ID, Data: &TopicData{}}}
|
|
storeChannel.topics[topic.ID] = storeTopic
|
|
if err = a.updateChannelTopic(storeChannel, storeTopic, &topic); err != nil {
|
|
return
|
|
}
|
|
storeTopic.topic.Revision = topic.Revision
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *TestApp) updateChannelTopic(storeChannel *testChannel, storeTopic *testTopic, topic *Topic) (err error) {
|
|
if storeTopic.topic.Revision != topic.Revision {
|
|
if storeTopic.topic.Data.TagRevision != topic.Data.TagRevision {
|
|
if err = a.updateChannelTopicTags(storeChannel, storeTopic); err != nil {
|
|
return
|
|
}
|
|
storeTopic.topic.Data.TagRevision = topic.Data.TagRevision
|
|
}
|
|
if topic.Data.TopicDetail != nil {
|
|
storeTopic.topic.Data.TopicDetail = topic.Data.TopicDetail
|
|
storeTopic.topic.Data.DetailRevision = topic.Data.DetailRevision
|
|
} else if storeTopic.topic.Data.DetailRevision != topic.Data.DetailRevision {
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics/{topicID}",
|
|
path: map[string]string{"channelID": storeChannel.channel.ID, "topicID": topic.ID},
|
|
tokenType: APPTokenAgent, token: a.token}
|
|
topic := Topic{}
|
|
response := &TestAPIResponse{data: &topic}
|
|
if err = TestAPIRequest(GetChannelTopic, params, response); err != nil {
|
|
return
|
|
}
|
|
if topic.Data == nil {
|
|
err = errors.New("topic removed during update")
|
|
return
|
|
}
|
|
storeTopic.topic.Data.TopicDetail = topic.Data.TopicDetail
|
|
storeTopic.topic.Data.DetailRevision = topic.Data.DetailRevision
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *TestApp) updateChannelTopicTags(storeChannel *testChannel, storeTopic *testTopic) (err error) {
|
|
var tags []Tag
|
|
if storeTopic.topic.Revision == 0 {
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics/{topicID}/tags",
|
|
path: map[string]string{"channelID": storeChannel.channel.ID, "topicID": storeTopic.topic.ID},
|
|
tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &tags}
|
|
if err = TestAPIRequest(GetChannelTopicTags, params, response); err != nil {
|
|
return
|
|
}
|
|
storeTopic.tags = make(map[string]*Tag)
|
|
} else {
|
|
revision := strconv.FormatInt(storeTopic.topic.Revision, 10)
|
|
params := &TestAPIParams{query: "/channels/{channelID}/topics/{topicID}/tags?revision=" + revision,
|
|
path: map[string]string{"channelID": storeChannel.channel.ID, "topicID": storeTopic.topic.ID},
|
|
tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &tags}
|
|
if err = TestAPIRequest(GetChannelTopicTags, params, response); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, tag := range tags {
|
|
if tag.Data == nil {
|
|
delete(storeTopic.tags, tag.ID)
|
|
} else {
|
|
storeTopic.tags[tag.ID] = &tag
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *TestApp) updateCards() (err error) {
|
|
var cards []Card
|
|
if a.revision.Card == 0 {
|
|
params := &TestAPIParams{query: "/cards", tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &cards}
|
|
if err = TestAPIRequest(GetCards, params, response); err != nil {
|
|
return
|
|
}
|
|
for _, card := range cards {
|
|
if card.Data == nil {
|
|
delete(a.contacts, card.ID)
|
|
} else {
|
|
// set new card
|
|
contactData := &TestContactData{card: card, articles: make(map[string]*Article), channels: make(map[string]*testChannel),
|
|
cardDetailRevision: card.Data.DetailRevision, cardProfileRevision: card.Data.ProfileRevision,
|
|
profileRevision: card.Data.ProfileRevision, token: a.token}
|
|
a.contacts[card.ID] = contactData
|
|
if err = contactData.updateContact(); err != nil {
|
|
contactData.offsync = true
|
|
PrintMsg(err)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
revision := strconv.FormatInt(a.revision.Card, 10)
|
|
params := &TestAPIParams{query: "/cards?revision=" + revision, tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &cards}
|
|
if err = TestAPIRequest(GetCards, params, response); err != nil {
|
|
return
|
|
}
|
|
for _, card := range cards {
|
|
if card.Data == nil {
|
|
delete(a.contacts, card.ID)
|
|
} else {
|
|
contactData, set := a.contacts[card.ID]
|
|
if !set {
|
|
// download new card
|
|
params := &TestAPIParams{query: "/cards/{cardID}", path: map[string]string{"cardID": card.ID},
|
|
tokenType: APPTokenAgent, token: a.token}
|
|
response := &TestAPIResponse{data: &card}
|
|
if err = TestAPIRequest(GetCard, params, response); err != nil {
|
|
return
|
|
}
|
|
contactData := &TestContactData{card: card, articles: make(map[string]*Article), channels: make(map[string]*testChannel),
|
|
cardDetailRevision: card.Data.DetailRevision, cardProfileRevision: card.Data.ProfileRevision,
|
|
profileRevision: card.Data.ProfileRevision, token: a.token}
|
|
a.contacts[card.ID] = contactData
|
|
if err = contactData.updateContact(); err != nil {
|
|
contactData.offsync = true
|
|
PrintMsg(err)
|
|
}
|
|
} else {
|
|
// update existing card revisions
|
|
contactData.card.Data.NotifiedProfile = card.Data.NotifiedProfile
|
|
contactData.card.Data.NotifiedArticle = card.Data.NotifiedArticle
|
|
contactData.card.Data.NotifiedChannel = card.Data.NotifiedChannel
|
|
contactData.card.Data.NotifiedView = card.Data.NotifiedView
|
|
contactData.card.Data.DetailRevision = card.Data.DetailRevision
|
|
contactData.card.Data.ProfileRevision = card.Data.ProfileRevision
|
|
if err = contactData.updateContact(); err != nil {
|
|
contactData.offsync = true
|
|
PrintMsg(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *TestApp) updateApp(rev *Revision) {
|
|
a.mutex.Lock()
|
|
defer a.mutex.Unlock()
|
|
|
|
if rev.Profile != a.revision.Profile {
|
|
if err := a.updateProfile(); err != nil {
|
|
PrintMsg(err)
|
|
} else {
|
|
a.revision.Profile = rev.Profile
|
|
}
|
|
}
|
|
|
|
if rev.Group != a.revision.Group {
|
|
if err := a.updateGroups(); err != nil {
|
|
PrintMsg(err)
|
|
} else {
|
|
a.revision.Group = rev.Group
|
|
}
|
|
}
|
|
|
|
if rev.Article != a.revision.Article {
|
|
if err := a.updateArticles(); err != nil {
|
|
PrintMsg(err)
|
|
} else {
|
|
a.revision.Article = rev.Article
|
|
}
|
|
}
|
|
|
|
if rev.Card != a.revision.Card {
|
|
if err := a.updateCards(); err != nil {
|
|
PrintMsg(err)
|
|
} else {
|
|
a.revision.Card = rev.Card
|
|
}
|
|
}
|
|
|
|
if rev.Channel != a.revision.Channel {
|
|
if err := a.updateChannels(); err != nil {
|
|
PrintMsg(err)
|
|
} else {
|
|
a.revision.Channel = rev.Channel
|
|
}
|
|
}
|
|
|
|
if a.condition != nil {
|
|
if a.condition.check(a) {
|
|
select {
|
|
case a.condition.channel <- true:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Connect provides a test handler for the websocket connection
|
|
func (a *TestApp) Connect(token string) error {
|
|
var revision Revision
|
|
var data []byte
|
|
var dataType int
|
|
|
|
a.token = token
|
|
|
|
// connect websocket
|
|
ws, err := StatusConnection(token, &revision)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.updateApp(&revision)
|
|
|
|
// reset any timeout
|
|
ws.SetReadDeadline(time.Time{})
|
|
|
|
// read revision update
|
|
for {
|
|
if dataType, data, err = ws.ReadMessage(); err != nil {
|
|
return errors.New("failed to read status connection")
|
|
}
|
|
if dataType != websocket.TextMessage {
|
|
return errors.New("invalid status data type")
|
|
}
|
|
rev := &Revision{}
|
|
if err = json.Unmarshal(data, rev); err != nil {
|
|
return errors.New("invalid status data")
|
|
}
|
|
a.updateApp(rev)
|
|
}
|
|
|
|
}
|
|
|
|
func (a *TestApp) setCondition(test *testCondition) {
|
|
a.mutex.Lock()
|
|
if test.check(a) {
|
|
test.channel <- true
|
|
} else {
|
|
a.condition = test
|
|
}
|
|
a.mutex.Unlock()
|
|
}
|
|
|
|
func (a *TestApp) clearCondition() {
|
|
a.mutex.Lock()
|
|
a.condition = nil
|
|
a.mutex.Unlock()
|
|
}
|
|
|
|
//WaitFor provides a helper waiting for a condition to be met
|
|
func (a *TestApp) WaitFor(check func(*TestApp) bool) error {
|
|
var done = make(chan bool, 1)
|
|
var wake = make(chan bool, 1)
|
|
a.setCondition(&testCondition{channel: done, check: check})
|
|
go func() {
|
|
time.Sleep(testTimeout * time.Second)
|
|
wake <- true
|
|
}()
|
|
select {
|
|
case <-done:
|
|
a.clearCondition()
|
|
return nil
|
|
case <-wake:
|
|
a.clearCondition()
|
|
return errors.New("timeout waiting for condition")
|
|
}
|
|
}
|
|
|
|
/*** endpoint test function ***/
|
|
|
|
//TestAPIParams holds the config for an endpoint test
|
|
type TestAPIParams struct {
|
|
restType string
|
|
path map[string]string
|
|
query string
|
|
body interface{}
|
|
tokenType string
|
|
token string
|
|
authorization string
|
|
credentials string
|
|
}
|
|
|
|
//TestAPIResponse holds the endpoint test response
|
|
type TestAPIResponse struct {
|
|
code int
|
|
data interface{}
|
|
header map[string][]string
|
|
}
|
|
|
|
//TestAPIRequest tests and endpoint with provided params
|
|
func TestAPIRequest(endpoint func(http.ResponseWriter, *http.Request), params *TestAPIParams, resp *TestAPIResponse) (err error) {
|
|
|
|
var r *http.Request
|
|
var w *httptest.ResponseRecorder
|
|
rest := params.restType
|
|
if rest == "" {
|
|
rest = "GET"
|
|
}
|
|
|
|
if params.tokenType == APPTokenAgent {
|
|
if !strings.Contains(params.query, "?") {
|
|
params.query += "?"
|
|
} else {
|
|
params.query += "&"
|
|
}
|
|
params.query += "agent=" + params.token
|
|
} else if params.tokenType == APPTokenContact {
|
|
if !strings.Contains(params.query, "?") {
|
|
params.query += "?"
|
|
} else {
|
|
params.query += "&"
|
|
}
|
|
params.query += "contact=" + params.token
|
|
}
|
|
|
|
if r, w, err = NewRequest(rest, params.query, params.body); err != nil {
|
|
return
|
|
}
|
|
r = mux.SetURLVars(r, params.path)
|
|
|
|
if params.tokenType != "" {
|
|
r.Header.Add("TokenType", params.tokenType)
|
|
}
|
|
if params.token != "" {
|
|
SetBearerAuth(r, params.token)
|
|
}
|
|
|
|
if params.authorization != "" {
|
|
SetBasicAuth(r, params.authorization)
|
|
}
|
|
if params.credentials != "" {
|
|
SetCredentials(r, params.credentials)
|
|
}
|
|
|
|
endpoint(w, r)
|
|
|
|
res := w.Result()
|
|
if res.StatusCode != 200 && res.StatusCode != 410 {
|
|
err = errors.New("response failed")
|
|
return
|
|
}
|
|
if resp != nil {
|
|
resp.header = res.Header
|
|
if resp.data != nil {
|
|
dec := json.NewDecoder(res.Body)
|
|
if err = dec.Decode(resp.data); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|