<commit_before>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package quota

import (
	"fmt"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/db"
	"labix.org/v2/mgo/bson"
	"launchpad.net/gocheck"
	"runtime"
	"sync"
	"testing"
)

func Test(t *testing.T) { gocheck.TestingT(t) }

var _ = gocheck.Suite(Suite{})

type Suite struct{}

func (Suite) SetUpSuite(c *gocheck.C) {
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "tsuru_quota_tests")
}

func (Suite) TearDownSuite(c *gocheck.C) {
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	conn.Apps().Database.DropDatabase()
}

func (Suite) TestCreate(c *gocheck.C) {
	err := Create("<EMAIL>", 10)
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	var u usage
	err = conn.Quota().Find(bson.M{"user": "<EMAIL>"}).One(&u)
	c.Assert(err, gocheck.IsNil)
	defer conn.Quota().Remove(bson.M{"user": "<EMAIL>"})
	c.Assert(u.User, gocheck.Equals, "<EMAIL>")
	c.Assert(u.Limit, gocheck.Equals, uint(10))
	c.Assert(u.Items, gocheck.HasLen, 0)
}

func (Suite) TestDuplicateQuota(c *gocheck.C) {
	err := Create("<EMAIL>", 10)
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	defer conn.Quota().Remove(bson.M{"user": "<EMAIL>"})
	err = Create("<EMAIL>", 50)
	c.Assert(err, gocheck.Equals, ErrQuotaAlreadyExists)
}

func (Suite) TestDelete(c *gocheck.C) {
	err := Create("<EMAIL>", 3)
	c.Assert(err, gocheck.IsNil)
	err = Delete("<EMAIL>")
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	count, err := conn.Quota().Find(bson.M{"user": "<EMAIL>"}).Count()
	c.Assert(err, gocheck.IsNil)
	c.Assert(count, gocheck.Equals, 0)
}

func (Suite) TestDeleteQuotaNotFound(c *gocheck.C) {
	err := Delete("<EMAIL>")
	c.Assert(err, gocheck.Equals, ErrQuotaNotFound)
}

func (Suite) TestReserve(c *gocheck.C) {
	err := Create("<EMAIL>", 3)
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	err = Reserve("<EMAIL>", "dt/1")
	c.Assert(err, gocheck.IsNil)
	err = Reserve("<EMAIL>", "dt/0")
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	var u usage
	err = conn.Quota().Find(bson.M{"user": "<EMAIL>"}).One(&u)
	c.Assert(err, gocheck.IsNil)
	c.Assert(u.Items, gocheck.DeepEquals, []string{"dt/1", "dt/0"})
}

func (Suite) TestReserveIsSafe(c *gocheck.C) {
	items := 300
	err := Create("<EMAIL>", uint(items-items/2))
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(24))
	var wg sync.WaitGroup
	wg.Add(items)
	for i := 0; i < items; i++ {
		go func(i int) {
			Reserve("<EMAIL>", fmt.Sprintf("spirit/%d", i))
			wg.Done()
		}(i)
	}
	wg.Wait()
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	var u usage
	err = conn.Quota().Find(bson.M{"user": "<EMAIL>"}).One(&u)
	c.Assert(err, gocheck.IsNil)
	c.Assert(u.Items, gocheck.HasLen, items-items/2)
}

func (Suite) TestReserveRepeatedItems(c *gocheck.C) {
	err := Create("<EMAIL>", 500)
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	err = Reserve("<EMAIL>", "spirit/0")
	c.Assert(err, gocheck.IsNil)
	err = Reserve("<EMAIL>", "spirit/0")
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	var u usage
	err = conn.Quota().Find(bson.M{"user": "<EMAIL>"}).One(&u)
	c.Assert(err, gocheck.IsNil)
	c.Assert(u.Items, gocheck.HasLen, 1)
}

func (Suite) TestReserveQuotaExceeded(c *gocheck.C) {
	err := Create("<EMAIL>", 1)
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	err = Reserve("<EMAIL>", "change/0")
	c.Assert(err, gocheck.IsNil)
	err = Reserve("<EMAIL>", "change/1")
	c.Assert(err, gocheck.Equals, ErrQuotaExceeded)
}

func (Suite) TestReserveQuotaNotFound(c *gocheck.C) {
	err := Reserve("<EMAIL>", "something")
	c.Assert(err, gocheck.Equals, ErrQuotaNotFound)
}

func (Suite) TestRelease(c *gocheck.C) {
	err := Create("<EMAIL>", 1)
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	err = Reserve("<EMAIL>", "beyond/0")
	c.Assert(err, gocheck.IsNil)
	err = Release("<EMAIL>", "beyond/0")
	c.Assert(err, gocheck.IsNil)
	err = Reserve("<EMAIL>", "beyond/1")
	c.Assert(err, gocheck.IsNil)
}

func (Suite) TestReleaseQuotaNotFound(c *gocheck.C) {
	err := Release("<EMAIL>", "see/0")
	c.Assert(err, gocheck.Equals, ErrQuotaNotFound)
}<commit_msg>quota: added test to ensure Release safety<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package quota

import (
	"fmt"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/db"
	"labix.org/v2/mgo/bson"
	"launchpad.net/gocheck"
	"runtime"
	"sync"
	"testing"
)

func Test(t *testing.T) { gocheck.TestingT(t) }

var _ = gocheck.Suite(Suite{})

type Suite struct{}

func (Suite) SetUpSuite(c *gocheck.C) {
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "tsuru_quota_tests")
}

func (Suite) TearDownSuite(c *gocheck.C) {
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	conn.Apps().Database.DropDatabase()
}

func (Suite) TestCreate(c *gocheck.C) {
	err := Create("<EMAIL>", 10)
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	var u usage
	err = conn.Quota().Find(bson.M{"user": "<EMAIL>"}).One(&u)
	c.Assert(err, gocheck.IsNil)
	defer conn.Quota().Remove(bson.M{"user": "<EMAIL>"})
	c.Assert(u.User, gocheck.Equals, "<EMAIL>")
	c.Assert(u.Limit, gocheck.Equals, uint(10))
	c.Assert(u.Items, gocheck.HasLen, 0)
}

func (Suite) TestDuplicateQuota(c *gocheck.C) {
	err := Create("<EMAIL>", 10)
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	defer conn.Quota().Remove(bson.M{"user": "<EMAIL>"})
	err = Create("<EMAIL>", 50)
	c.Assert(err, gocheck.Equals, ErrQuotaAlreadyExists)
}

func (Suite) TestDelete(c *gocheck.C) {
	err := Create("<EMAIL>", 3)
	c.Assert(err, gocheck.IsNil)
	err = Delete("<EMAIL>")
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	count, err := conn.Quota().Find(bson.M{"user": "<EMAIL>"}).Count()
	c.Assert(err, gocheck.IsNil)
	c.Assert(count, gocheck.Equals, 0)
}

func (Suite) TestDeleteQuotaNotFound(c *gocheck.C) {
	err := Delete("<EMAIL>")
	c.Assert(err, gocheck.Equals, ErrQuotaNotFound)
}

func (Suite) TestReserve(c *gocheck.C) {
	err := Create("<EMAIL>", 3)
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	err = Reserve("<EMAIL>", "dt/1")
	c.Assert(err, gocheck.IsNil)
	err = Reserve("<EMAIL>", "dt/0")
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	var u usage
	err = conn.Quota().Find(bson.M{"user": "<EMAIL>"}).One(&u)
	c.Assert(err, gocheck.IsNil)
	c.Assert(u.Items, gocheck.DeepEquals, []string{"dt/1", "dt/0"})
}

func (Suite) TestReserveIsSafe(c *gocheck.C) {
	items := 300
	err := Create("<EMAIL>", uint(items-items/2))
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(24))
	var wg sync.WaitGroup
	wg.Add(items)
	for i := 0; i < items; i++ {
		go func(i int) {
			Reserve("<EMAIL>", fmt.Sprintf("spirit/%d", i))
			wg.Done()
		}(i)
	}
	wg.Wait()
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	var u usage
	err = conn.Quota().Find(bson.M{"user": "<EMAIL>"}).One(&u)
	c.Assert(err, gocheck.IsNil)
	c.Assert(u.Items, gocheck.HasLen, items-items/2)
}

func (Suite) TestReserveRepeatedItems(c *gocheck.C) {
	err := Create("<EMAIL>", 500)
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	err = Reserve("<EMAIL>", "spirit/0")
	c.Assert(err, gocheck.IsNil)
	err = Reserve("<EMAIL>", "spirit/0")
	c.Assert(err, gocheck.IsNil)
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	var u usage
	err = conn.Quota().Find(bson.M{"user": "<EMAIL>"}).One(&u)
	c.Assert(err, gocheck.IsNil)
	c.Assert(u.Items, gocheck.HasLen, 1)
}

func (Suite) TestReserveQuotaExceeded(c *gocheck.C) {
	err := Create("<EMAIL>", 1)
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	err = Reserve("<EMAIL>", "change/0")
	c.Assert(err, gocheck.IsNil)
	err = Reserve("<EMAIL>", "change/1")
	c.Assert(err, gocheck.Equals, ErrQuotaExceeded)
}

func (Suite) TestReserveQuotaNotFound(c *gocheck.C) {
	err := Reserve("<EMAIL>", "something")
	c.Assert(err, gocheck.Equals, ErrQuotaNotFound)
}

func (Suite) TestRelease(c *gocheck.C) {
	err := Create("<EMAIL>", 1)
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	err = Reserve("<EMAIL>", "beyond/0")
	c.Assert(err, gocheck.IsNil)
	err = Release("<EMAIL>", "beyond/0")
	c.Assert(err, gocheck.IsNil)
	err = Reserve("<EMAIL>", "beyond/1")
	c.Assert(err, gocheck.IsNil)
}

func (Suite) TestReleaseQuotaNotFound(c *gocheck.C) {
	err := Release("<EMAIL>", "see/0")
	c.Assert(err, gocheck.Equals, ErrQuotaNotFound)
}

func (Suite) TestReleaseIsSafe(c *gocheck.C) {
	items := 100
	err := Create("<EMAIL>", uint(items))
	c.Assert(err, gocheck.IsNil)
	defer Delete("<EMAIL>")
	for i := 0; i < items; i++ {
		Reserve("<EMAIL>", fmt.Sprintf("looking/%d", i))
	}
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(16))
	var wg sync.WaitGroup
	for i := 0; i < items+items/2; i++ {
		wg.Add(1)
		go func(i int) {
			err := Release("<EMAIL>", fmt.Sprintf("looking/%d", i))
			c.Check(err, gocheck.IsNil)
			wg.Done()
		}(i)
	}
	wg.Wait()
	conn, err := db.Conn()
	c.Assert(err, gocheck.IsNil)
	defer conn.Close()
	var u usage
	err = conn.Quota().Find(bson.M{"user": "<EMAIL>"}).One(&u)
	c.Assert(err, gocheck.IsNil)
	c.Assert(u.Items, gocheck.HasLen, 0)
}
{'fraction_non_alphanumeric': 0.20565006075334144, 'fraction_numerical': 0.011948157148643175, 'mean_word_length': 4.796361502347418, 'pattern_counts': {'":': 15, '<': 84, '<?xml version=': 0, '>': 80, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6570075', 'n_tokens_mistral': 4302, 'n_tokens_neox': 4197, 'n_words': 800}