7 Commits

Author SHA1 Message Date
James Mills
c69d9a1259 New test framework with better reliability 2017-11-28 02:11:40 -08:00
James Mills
e6491c955a Moar tests 2017-11-28 01:03:28 -08:00
James Mills
c0b8342b32 Rename some tests 2017-11-28 00:17:25 -08:00
James Mills
6f16f24a98 Add TestUser_PRIVMSG test 2017-11-28 00:13:21 -08:00
James Mills
b5239acd9b Update vendor 3rd-party deps for missing github.com/stretchr/testify/assert 2017-11-27 23:56:51 -08:00
James Mills
73ace6b829 Add TestConnect_RplWelcome test 2017-11-27 23:43:19 -08:00
James Mills
bf78fbc9f4 Added a basic integration test suite framework 2017-11-27 23:21:48 -08:00
43 changed files with 156 additions and 2183 deletions

View File

@@ -6,15 +6,9 @@ pipeline:
build:
image: golang
commands:
- go get -d ./...
- go get -d
- go build .
test:
image: golang
commands:
- go get -d ./...
- go test ./...
docker:
image: plugins/docker
repo: r.mills.io/prologic/eris
@@ -23,8 +17,7 @@ pipeline:
notify:
image: drillster/drone-email
host: mail_poste
port: 25
host: mail.mills.io
from: drone@mills.io
skip_verify: true
when:

1
.gitignore vendored
View File

@@ -1,6 +1,5 @@
*~*
bin
dist
*.db
*.pem

15
.gitmodules vendored
View File

@@ -43,18 +43,15 @@
[submodule "vendor/github.com/prometheus/procfs"]
path = vendor/github.com/prometheus/procfs
url = https://github.com/prometheus/procfs
[submodule "vendor/github.com/sasha-s/go-deadlock"]
path = vendor/github.com/sasha-s/go-deadlock
url = https://github.com/sasha-s/go-deadlock
[submodule "vendor/github.com/petermattis/goid"]
path = vendor/github.com/petermattis/goid
url = https://github.com/petermattis/goid
[submodule "vendor/github.com/thoj/go-ircevent"]
path = vendor/github.com/thoj/go-ircevent
url = https://github.com/thoj/go-ircevent
[submodule "vendor/github.com/stretchr/testify"]
path = vendor/github.com/stretchr/testify
url = https://github.com/stretchr/testify
[submodule "vendor/github.com/renstrom/shortuuid"]
path = vendor/github.com/renstrom/shortuuid
url = https://github.com/renstrom/shortuuid
[submodule "vendor/github.com/mmcloughlin/professor"]
path = vendor/github.com/mmcloughlin/professor
url = https://github.com/mmcloughlin/professor
[submodule "vendor/github.com/google/uuid"]
path = vendor/github.com/google/uuid
url = https://github.com/google/uuid

View File

@@ -1,31 +0,0 @@
builds:
- binary: eris
flags: -tags "static_build"
ldflags: -w -X mail.Version={{.Version}} -X main.Commit={{.Commit}}
env:
- CGO_ENABLED=0
goos:
- darwin
- freebsd
- linux
- windows
goarch:
- i386
- amd64
- arm
- amd64
goarm:
- 6
- 7
sign:
artifacts: checksum
archive:
wrap_in_directory: true
format_overrides:
- goos: windows
format: zip
files:
- "*.pem"
- "*.yml"
- "LICENSE"
- "README.md"

View File

@@ -1,46 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at prologic@shortcircuit.net.au. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -2,6 +2,7 @@
FROM golang:alpine AS build
ARG TAG
ARG BUILD
ENV APP eris
ENV REPO prologic/$APP
@@ -11,7 +12,7 @@ RUN apk add --update git make build-base && \
WORKDIR /go/src/github.com/$REPO
COPY . /go/src/github.com/$REPO
RUN make TAG=$TAG build
RUN make TAG=$TAG BUILD=$BUILD build
# Runtime
FROM alpine

View File

@@ -6,6 +6,7 @@ APP=eris
PACKAGE=irc
REPO?=prologic/$(APP)
TAG?=latest
BUILD?=dev
all: dev
@@ -16,24 +17,17 @@ deps:
@go get ./...
build: clean deps
@echo "github.com/$(REPO)/${PACKAGE}.GitCommit=$(COMMIT)"
@echo " -> Building $(REPO) $(TAG)@$(COMMIT)"
@echo " -> Building $(TAG)$(BUILD)"
@go build -tags "netgo static_build" -installsuffix netgo \
-ldflags "-w -X github.com/$(REPO)/${PACKAGE}.GitCommit=$(COMMIT)"
-ldflags "-w -X github.com/$(REPO)/${PACKAGE}.GitCommit=$(COMMIT) -X github.com/$(REPO)/${PACKAGE}.Build=$(BUILD)" .
@echo "Built $$(./$(APP) -v)"
image:
@docker build --build-arg TAG=$(TAG) -t $(REPO):$(TAG) .
@docker build --build-arg TAG=$(TAG) --build-arg BUILD=$(BUILD) -t $(REPO):$(TAG) .
@echo "Image created: $(REPO):$(TAG)"
profile:
@go test -cpuprofile cpu.prof -memprofile mem.prof -v -bench ./...
bench:
@go test -v -bench ./...
test:
@go test -v -cover -race ./...
@go test -v -cover -race $(TEST_ARGS)
clean:
@rm -rf $(APP)

View File

@@ -1,4 +1,4 @@
eris - IRC Server / Daemon written in Go
# eris - IRC Server / Daemon written in Go
[![Build Status](https://travis-ci.org/prologic/eris.svg)](https://travis-ci.org/prologic/eris)
[![Go Report Card](https://goreportcard.com/badge/github.com/prologic/eris)](https://goreportcard.com/report/github.com/prologic/eris)
@@ -25,7 +25,7 @@ The connotation here is that IRC (*Internet Relay Chat*) is a place of chaos,
strife and discord. IRC is a place where you argue and get into arguments for
the sake of argument.
So `eris` is an IRC daemon written from scratch in Go to facilitate discord
So `eris` is an IRC daemon written from scratch in Go to factiliate discord
and have arguments for the sake of argument!
Pull requests and issues are welcome.
@@ -145,31 +145,10 @@ There are a number of supported accompanying services that are being developed a
* [Soter](https://github.com/prologic/soter) -- An IRC Bot that persists channel modes and topics.
* [Cadmus](https://github.com/prologic/cadmus) -- An IRC Bot that logs channels and provides an interface for viewing and searching logs
## Recommended Clients
### CLI / Terminal
* [irccat](https://github.com/prologic/irccat)
* [irssi](https://irssi.org/)
### Cloud
* [IRCCloud](https://www.irccloud.com/)
### Desktop
* [HexChat (Linux)](https://hexchat.github.io/)
* [Textual (OSX)](https://www.codeux.com/textual/)
* [mIRC (Windows)](https://www.mirc.com/)
### Mobile
## Recommended Mobile clients
* [Palaver (iOS)](https://palaverapp.com/) -- SASL, TLS, Server Password, Push Notifications, IRCv3 (*Also supports custom image upload service(s) for better privacy of shared photos/images over IRC*)
### Web
* [Dispatch](https://github.com/khlieng/dispatch) -- TLS, Multiple Servers and Users, Client Certificates
## License
eris is licensed under the MIT License.

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,6 @@ import (
"crypto/tls"
"fmt"
"net"
"sync"
"time"
log "github.com/sirupsen/logrus"
@@ -15,30 +14,6 @@ const (
QUIT_TIMEOUT = time.Minute // how long after idle before a client is kicked
)
type SyncBool struct {
sync.RWMutex
value bool
}
func NewSyncBool(value bool) *SyncBool {
return &SyncBool{value: value}
}
func (sb *SyncBool) Get() bool {
sb.RLock()
defer sb.RUnlock()
return sb.value
}
func (sb *SyncBool) Set(value bool) {
sb.Lock()
defer sb.Unlock()
sb.value = value
}
type Client struct {
atime time.Time
authorized bool
@@ -48,7 +23,7 @@ type Client struct {
channels *ChannelSet
ctime time.Time
flags map[UserMode]bool
hasQuit *SyncBool
hasQuit bool
hops uint
hostname Name
hostmask Name // Cloacked hostname (SHA256)
@@ -75,7 +50,6 @@ func NewClient(server *Server, conn net.Conn) *Client {
channels: NewChannelSet(),
ctime: now,
flags: make(map[UserMode]bool),
hasQuit: NewSyncBool(false),
sasl: NewSaslState(),
server: server,
socket: NewSocket(conn),
@@ -98,14 +72,8 @@ func NewClient(server *Server, conn net.Conn) *Client {
//
func (client *Client) writeloop() {
for {
select {
case reply, ok := <-client.replies:
if !ok || reply == "" || client.socket == nil {
return
}
client.socket.Write(reply)
}
for reply := range client.replies {
client.socket.Write(reply)
}
}
@@ -151,6 +119,13 @@ func (client *Client) readloop() {
}
func (client *Client) processCommand(cmd Command) {
client.server.metrics.Counter("client", "commands").Inc()
defer func(t time.Time) {
v := client.server.metrics.SummaryVec("client", "command_duration_seconds")
v.WithLabelValues(cmd.Code().String()).Observe(time.Now().Sub(t).Seconds())
}(time.Now())
cmd.SetClient(client)
if !client.registered {
@@ -169,13 +144,6 @@ func (client *Client) processCommand(cmd Command) {
return
}
client.server.metrics.Counter("client", "commands").Inc()
defer func(t time.Time) {
v := client.server.metrics.SummaryVec("client", "command_duration_seconds")
v.WithLabelValues(cmd.Code().String()).Observe(time.Now().Sub(t).Seconds())
}(time.Now())
switch srvCmd.(type) {
case *PingCommand, *PongCommand:
client.Touch()
@@ -241,7 +209,6 @@ func (client *Client) Register() {
return
}
client.registered = true
client.flags[HostMask] = true
client.Touch()
}
@@ -274,6 +241,7 @@ func (client *Client) destroy() {
}
close(client.replies)
client.replies = nil
client.socket.Close()
@@ -390,17 +358,17 @@ func (client *Client) ChangeNickname(nickname Name) {
}
func (client *Client) Reply(reply string) {
if !client.hasQuit.Get() {
if client.replies != nil {
client.replies <- reply
}
}
func (client *Client) Quit(message Text) {
if client.hasQuit.Get() {
if client.hasQuit {
return
}
client.hasQuit.Set(true)
client.hasQuit = true
client.Reply(RplError("quit"))
client.server.whoWas.Append(client)
friends := client.Friends()

View File

@@ -4,7 +4,9 @@ import (
"errors"
"regexp"
"strings"
"sync"
//"sync"
sync "github.com/sasha-s/go-deadlock"
"github.com/DanielOaks/girc-go/ircmatch"
)

View File

@@ -4,7 +4,9 @@ import (
"errors"
"io/ioutil"
"log"
"sync"
//"sync"
sync "github.com/sasha-s/go-deadlock"
"github.com/imdario/mergo"
"gopkg.in/yaml.v2"

View File

@@ -3,7 +3,6 @@ package irc
import (
"fmt"
"net/http"
"sync"
log "github.com/sirupsen/logrus"
@@ -11,7 +10,6 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// DefObjectives ...
var DefObjectives = map[float64]float64{
0.50: 0.05,
0.90: 0.01,
@@ -19,27 +17,22 @@ var DefObjectives = map[float64]float64{
0.99: 0.001,
}
// Metrics ...
type Metrics struct {
sync.RWMutex
namespace string
metrics map[string]prometheus.Metric
guagevecs map[string]*prometheus.GaugeVec
gaugevecs map[string]*prometheus.GaugeVec
sumvecs map[string]*prometheus.SummaryVec
}
// NewMetrics ...
func NewMetrics(namespace string) *Metrics {
return &Metrics{
namespace: namespace,
metrics: make(map[string]prometheus.Metric),
guagevecs: make(map[string]*prometheus.GaugeVec),
gaugevecs: make(map[string]*prometheus.GaugeVec),
sumvecs: make(map[string]*prometheus.SummaryVec),
}
}
// NewCounter ...
func (m *Metrics) NewCounter(subsystem, name, help string) prometheus.Counter {
counter := prometheus.NewCounter(
prometheus.CounterOpts{
@@ -51,15 +44,12 @@ func (m *Metrics) NewCounter(subsystem, name, help string) prometheus.Counter {
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = counter
m.Unlock()
prometheus.MustRegister(counter)
return counter
}
// NewCounterFunc ...
func (m *Metrics) NewCounterFunc(subsystem, name, help string, f func() float64) prometheus.CounterFunc {
counter := prometheus.NewCounterFunc(
prometheus.CounterOpts{
@@ -72,15 +62,12 @@ func (m *Metrics) NewCounterFunc(subsystem, name, help string, f func() float64)
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = counter
m.Unlock()
prometheus.MustRegister(counter)
return counter
}
// NewGauge ...
func (m *Metrics) NewGauge(subsystem, name, help string) prometheus.Gauge {
guage := prometheus.NewGauge(
prometheus.GaugeOpts{
@@ -92,15 +79,12 @@ func (m *Metrics) NewGauge(subsystem, name, help string) prometheus.Gauge {
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = guage
m.Unlock()
prometheus.MustRegister(guage)
return guage
}
// NewGaugeFunc ...
func (m *Metrics) NewGaugeFunc(subsystem, name, help string, f func() float64) prometheus.GaugeFunc {
guage := prometheus.NewGaugeFunc(
prometheus.GaugeOpts{
@@ -113,17 +97,14 @@ func (m *Metrics) NewGaugeFunc(subsystem, name, help string, f func() float64) p
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = guage
m.Unlock()
prometheus.MustRegister(guage)
return guage
}
// NewGaugeVec ...
func (m *Metrics) NewGaugeVec(subsystem, name, help string, labels []string) *prometheus.GaugeVec {
guagevec := prometheus.NewGaugeVec(
gauagevec := prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: m.namespace,
Subsystem: subsystem,
@@ -134,15 +115,12 @@ func (m *Metrics) NewGaugeVec(subsystem, name, help string, labels []string) *pr
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.guagevecs[key] = guagevec
m.Unlock()
prometheus.MustRegister(guagevec)
m.gaugevecs[key] = gauagevec
prometheus.MustRegister(gauagevec)
return guagevec
return gauagevec
}
// NewSummary ...
func (m *Metrics) NewSummary(subsystem, name, help string) prometheus.Summary {
summary := prometheus.NewSummary(
prometheus.SummaryOpts{
@@ -155,15 +133,12 @@ func (m *Metrics) NewSummary(subsystem, name, help string) prometheus.Summary {
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = summary
m.Unlock()
prometheus.MustRegister(summary)
return summary
}
// NewSummaryVec ...
func (m *Metrics) NewSummaryVec(subsystem, name, help string, labels []string) *prometheus.SummaryVec {
sumvec := prometheus.NewSummaryVec(
prometheus.SummaryOpts{
@@ -177,58 +152,41 @@ func (m *Metrics) NewSummaryVec(subsystem, name, help string, labels []string) *
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.sumvecs[key] = sumvec
m.Unlock()
prometheus.MustRegister(sumvec)
return sumvec
}
// Counter ...
func (m *Metrics) Counter(subsystem, name string) prometheus.Counter {
key := fmt.Sprintf("%s_%s", subsystem, name)
return m.metrics[key].(prometheus.Counter)
}
// Gauge ...
func (m *Metrics) Gauge(subsystem, name string) prometheus.Gauge {
key := fmt.Sprintf("%s_%s", subsystem, name)
m.RLock()
defer m.RUnlock()
return m.metrics[key].(prometheus.Gauge)
}
// GaugeVec ...
func (m *Metrics) GaugeVec(subsystem, name string) *prometheus.GaugeVec {
key := fmt.Sprintf("%s_%s", subsystem, name)
m.RLock()
defer m.RUnlock()
return m.guagevecs[key]
return m.gaugevecs[key]
}
// Summary ...
func (m *Metrics) Summary(subsystem, name string) prometheus.Summary {
key := fmt.Sprintf("%s_%s", subsystem, name)
m.RLock()
defer m.RUnlock()
return m.metrics[key].(prometheus.Summary)
}
// SummaryVec ...
func (m *Metrics) SummaryVec(subsystem, name string) *prometheus.SummaryVec {
key := fmt.Sprintf("%s_%s", subsystem, name)
m.RLock()
defer m.RUnlock()
return m.sumvecs[key]
}
// Handler ...
func (m *Metrics) Handler() http.Handler {
return promhttp.Handler()
}
// Run ...
func (m *Metrics) Run(addr string) {
http.Handle("/", m.Handler())
log.Infof("metrics endpoint listening on %s", addr)

View File

@@ -1,51 +0,0 @@
package irc
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestMetrics(t *testing.T) {
assert := assert.New(t)
m := NewMetrics("test")
m.NewCounter("foo", "counter", "help")
m.NewCounterFunc("foo", "counter_func", "help", func() float64 { return 1.0 })
m.NewGauge("foo", "gauge", "help")
m.NewGaugeFunc("foo", "gauge_func", "help", func() float64 { return 1.0 })
m.NewGaugeVec("foo", "gauge_vec", "help", []string{"test"})
m.Counter("foo", "counter").Inc()
m.Gauge("foo", "gauge").Add(1)
m.GaugeVec("foo", "gauge_vec").WithLabelValues("test").Add(1)
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/", nil)
m.Handler().ServeHTTP(w, r)
assert.Equal(w.Code, http.StatusOK)
assert.Regexp(
`
# HELP test_foo_counter help
# TYPE test_foo_counter counter
test_foo_counter 1
# HELP test_foo_counter_func help
# TYPE test_foo_counter_func counter
test_foo_counter_func 1
# HELP test_foo_gauge help
# TYPE test_foo_gauge gauge
test_foo_gauge 1
# HELP test_foo_gauge_func help
# TYPE test_foo_gauge_func gauge
test_foo_gauge_func 1
# HELP test_foo_gauge_vec help
# TYPE test_foo_gauge_vec gauge
test_foo_gauge_vec{test="test"} 1
`,
w.Body.String(),
)
}

View File

@@ -58,12 +58,11 @@ const (
Registered UserMode = 'r' // not a real user mode (flag)
SecureConn UserMode = 'z'
SecureOnly UserMode = 'Z'
HostMask UserMode = 'x'
)
var (
SupportedUserModes = UserModes{
Invisible, Operator, HostMask,
Invisible, Operator,
}
DefaultChannelModes = ChannelModes{
NoOutside, OpOnlyTopic,
@@ -117,7 +116,7 @@ func (m *ModeCommand) HandleServer(s *Server) {
for _, change := range m.changes {
switch change.mode {
case Invisible, HostMask, WallOps, SecureOnly:
case Invisible, WallOps, SecureOnly:
switch change.op {
case Add:
if target.flags[change.mode] {

View File

@@ -4,8 +4,9 @@ import (
"encoding/base64"
"fmt"
"golang.org/x/crypto/bcrypt"
"sync"
//"sync"
sync "github.com/sasha-s/go-deadlock"
log "github.com/sirupsen/logrus"
)

View File

@@ -266,7 +266,7 @@ func (target *Client) RplWhois(client *Client) {
func (target *Client) RplWhoisUser(client *Client) {
var clientHost Name
if target.flags[Operator] || !client.flags[HostMask] {
if target.flags[Operator] {
clientHost = client.hostname
} else {
clientHost = client.hostmask
@@ -342,7 +342,7 @@ func (target *Client) RplChannelModeIs(channel *Channel) {
func (target *Client) RplWhoReply(channel *Channel, client *Client) {
var clientHost Name
if target.flags[Operator] || !client.flags[HostMask] {
if target.flags[Operator] {
clientHost = client.hostname
} else {
clientHost = client.hostmask

View File

@@ -2,7 +2,9 @@ package irc
import (
"bytes"
"sync"
//"sync"
sync "github.com/sasha-s/go-deadlock"
)
type SaslState struct {

View File

@@ -51,8 +51,8 @@ type Server struct {
var (
SERVER_SIGNALS = []os.Signal{
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGINT, syscall.SIGHUP,
syscall.SIGTERM, syscall.SIGQUIT,
}
)
@@ -80,7 +80,7 @@ func NewServer(config *Config) *Server {
log.Debugf("accounts: %v", config.Accounts())
// TODO: Make this configureable?
// TODO: Make this configurabel?
server.ids["global"] = NewIdentity(config.Server.Name, "global")
if config.Server.Password != "" {
@@ -136,7 +136,7 @@ func NewServer(config *Config) *Server {
},
)
// server clients gauge (by secure/insecure)
// server clients gauge (by secure/insecire)
server.metrics.NewGaugeVec(
"server", "clients",
"Number of registered clients connected (by secure/insecure)",

View File

@@ -3,7 +3,9 @@ package irc
import (
"fmt"
"strings"
"sync"
//"sync"
sync "github.com/sasha-s/go-deadlock"
)
//

View File

@@ -5,11 +5,14 @@ import (
)
var (
// Package package name
//PackageName package name
Package = "eris"
// Version release version
Version = "1.6.4"
Version = "1.6.3"
// Build will be overwritten automatically by the build system
Build = "dev"
// GitCommit will be overwritten automatically by the build system
GitCommit = "HEAD"
@@ -17,5 +20,5 @@ var (
// FullVersion display the full version and build
func FullVersion() string {
return fmt.Sprintf("%s-%s@%s", Package, Version, GitCommit)
return fmt.Sprintf("%s-%s-%s@%s", Package, Version, Build, GitCommit)
}

View File

@@ -1,7 +1,9 @@
package irc
import (
"sync"
//"sync"
sync "github.com/sasha-s/go-deadlock"
)
type WhoWasList struct {

View File

@@ -7,7 +7,6 @@ import (
log "github.com/sirupsen/logrus"
"github.com/mmcloughlin/professor"
"github.com/prologic/eris/irc"
)
@@ -34,10 +33,6 @@ func main() {
log.SetLevel(log.WarnLevel)
}
if debug {
go professor.Launch(":6060")
}
config, err := irc.LoadConfig(configfile)
if err != nil {
log.Fatal("Config file did not load successfully:", err.Error())

View File

@@ -1,31 +1,26 @@
package main
import (
"crypto/sha256"
"flag"
"fmt"
"log"
"os"
"strings"
"testing"
"time"
log "github.com/sirupsen/logrus"
"github.com/renstrom/shortuuid"
"github.com/stretchr/testify/assert"
"github.com/thoj/go-ircevent"
eris "github.com/prologic/eris/irc"
)
const (
TIMEOUT = 3 * time.Second
)
var (
done chan bool
server *eris.Server
debug = flag.Bool("d", false, "enable debug logging")
client *irc.Connection
clients map[string]*irc.Connection
tls = flag.Bool("tls", false, "run tests with TLS")
)
func setupServer() *eris.Server {
@@ -36,11 +31,6 @@ func setupServer() *eris.Server {
config.Server.Description = "Test"
config.Server.Listen = []string{":6667"}
// SASL
config.Account = map[string]*eris.PassConfig{
"admin": &eris.PassConfig{"JDJhJDA0JGtUU1JVc1JOUy9DbEh1WEdvYVlMdGVnclp6YnA3NDBOZGY1WUZhdTZtRzVmb1VKdXQ5ckZD"},
}
server := eris.NewServer(config)
go server.Run()
@@ -48,21 +38,9 @@ func setupServer() *eris.Server {
return server
}
func randomValidName() string {
var name eris.Name
for {
name = eris.NewName(shortuuid.New())
if name.IsNickname() {
break
}
}
return name.String()
}
func newClient(start bool) *irc.Connection {
name := randomValidName()
client := irc.IRC(name, name)
client.RealName = fmt.Sprintf("Test Client: %s", name)
func newClient(nick, user, name string, start bool) *irc.Connection {
client := irc.IRC(nick, user)
client.RealName = name
err := client.Connect("localhost:6667")
if err != nil {
@@ -79,16 +57,21 @@ func newClient(start bool) *irc.Connection {
func TestMain(m *testing.M) {
flag.Parse()
if *debug {
log.SetLevel(log.DebugLevel)
} else {
log.SetLevel(log.WarnLevel)
}
done = make(chan bool)
server = setupServer()
client = newClient("test", "test", "Test", true)
clients = make(map[string]*irc.Connection)
clients["test1"] = newClient("test1", "test", "Test 1", true)
clients["test2"] = newClient("test2", "test", "Test 2", true)
result := m.Run()
for _, client := range clients {
client.Quit()
}
server.Stop()
os.Exit(result)
@@ -97,481 +80,106 @@ func TestMain(m *testing.M) {
func TestConnection(t *testing.T) {
assert := assert.New(t)
expected := true
actual := make(chan bool)
var (
expected bool
actual chan bool
)
client := newClient(false)
expected = true
actual = make(chan bool)
client := newClient("connect", "connect", "Connect", false)
client.AddCallback("001", func(e *irc.Event) {
defer func() { done <- true }()
actual <- true
})
time.AfterFunc(1*time.Second, func() { done <- true })
defer client.Quit()
go client.Loop()
<-done
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
func TestSASL(t *testing.T) {
assert := assert.New(t)
expected := true
actual := make(chan bool)
client := newClient(false)
client.SASLLogin = "admin"
client.SASLPassword = "admin"
client.AddCallback("001", func(e *irc.Event) {
actual <- true
})
defer client.Quit()
go client.Loop()
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
assert.Equal(expected, <-actual)
}
func TestRplWelcome(t *testing.T) {
assert := assert.New(t)
expected := "Welcome to the .* Internet Relay Network .*!.*@.*"
actual := make(chan string)
var (
expected string
actual chan string
)
client := newClient(false)
expected = "Welcome to the .* Internet Relay Network .*!.*@.*"
actual = make(chan string)
client := newClient("connect", "connect", "Connect", false)
client.AddCallback("001", func(e *irc.Event) {
defer func() { done <- true }()
actual <- e.Message()
})
time.AfterFunc(1*time.Second, func() { done <- true })
defer client.Quit()
go client.Loop()
<-done
select {
case res := <-actual:
assert.Regexp(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
assert.Regexp(expected, <-actual)
}
func TestUser_JOIN(t *testing.T) {
assert := assert.New(t)
client := newClient(false)
var (
expected []string
actual chan string
)
expected := []string{client.GetNick(), "=", "#join", fmt.Sprintf("@%s", client.GetNick())}
actual := make(chan string)
expected = []string{"test", "=", "#test", "@test"}
actual = make(chan string)
client.AddCallback("353", func(e *irc.Event) {
defer func() { done <- true }()
for i := range e.Arguments {
actual <- e.Arguments[i]
}
})
defer client.Quit()
go client.Loop()
client.Join("#join")
client.SendRaw("NAMES #join")
time.AfterFunc(1*time.Second, func() { done <- true })
client.Join("#test")
client.SendRaw("NAMES #test")
<-done
for i := range expected {
select {
case res := <-actual:
assert.Equal(expected[i], res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
assert.Equal(expected[i], <-actual)
}
}
func TestChannel_InviteOnly(t *testing.T) {
assert := assert.New(t)
expected := true
actual := make(chan bool)
client1 := newClient(false)
client2 := newClient(false)
client1.AddCallback("324", func(e *irc.Event) {
if strings.Contains(e.Arguments[2], "i") {
client2.Join("#inviteonly")
} else {
client1.Mode("#inviteonly")
}
})
client2.AddCallback("473", func(e *irc.Event) {
actual <- true
})
client2.AddCallback("JOIN", func(e *irc.Event) {
actual <- false
})
defer client1.Quit()
defer client2.Quit()
go client1.Loop()
go client2.Loop()
client1.Join("#inviteonly")
client1.Mode("#inviteonly", "+i")
client1.Mode("#inviteonly")
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
func TestUser_WithHostMask(t *testing.T) {
assert := assert.New(t)
client1 := newClient(false)
client2 := newClient(false)
expected := fmt.Sprintf("%x", sha256.Sum256([]byte("localhost")))
actual := make(chan string)
client1.AddCallback("001", func(e *irc.Event) {
client1.Mode(client1.GetNick(), "+x")
})
client2.AddCallback("001", func(e *irc.Event) {
client2.Whois(client1.GetNick())
})
client2.AddCallback("401", func(e *irc.Event) {
client2.Whois(client1.GetNick())
})
client2.AddCallback("311", func(e *irc.Event) {
actual <- e.Arguments[3]
})
defer client1.Quit()
defer client2.Quit()
go client1.Loop()
go client2.Loop()
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
/* FIXME: This test is racey :/
func TestUser_WithoutHostMask(t *testing.T) {
assert := assert.New(t)
client1 := newClient(false)
client2 := newClient(false)
expected := "localhost"
actual := make(chan string)
client1.AddCallback("001", func(e *irc.Event) {
client1.Mode(client1.GetNick(), "-x")
})
client2.AddCallback("001", func(e *irc.Event) {
client2.Whois(client1.GetNick())
})
client2.AddCallback("401", func(e *irc.Event) {
client2.Whois(client1.GetNick())
})
client2.AddCallback("311", func(e *irc.Event) {
actual <- e.Arguments[3]
})
defer client1.Quit()
defer client2.Quit()
go client1.Loop()
go client2.Loop()
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
*/
func TestUser_PRIVMSG(t *testing.T) {
assert := assert.New(t)
expected := "Hello World!"
actual := make(chan string)
var (
expected string
actual chan string
)
client1 := newClient(false)
client2 := newClient(false)
expected = "Hello World!"
actual = make(chan string)
client1.AddCallback("001", func(e *irc.Event) {
client1.Privmsg(client2.GetNick(), expected)
clients["test1"].AddCallback("PRIVMSG", func(e *irc.Event) {
defer func() { done <- true }()
})
client1.AddCallback("PRIVMSG", func(e *irc.Event) {
actual <- e.Message()
})
client2.AddCallback("001", func(e *irc.Event) {
client2.Privmsg(client1.GetNick(), expected)
})
client2.AddCallback("PRIVMSG", func(e *irc.Event) {
actual <- e.Message()
})
time.AfterFunc(1*time.Second, func() { done <- true })
client.Privmsg("test1", expected)
<-done
defer client1.Quit()
defer client2.Quit()
go client1.Loop()
go client2.Loop()
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
func TestChannel_PRIVMSG(t *testing.T) {
assert := assert.New(t)
expected := "Hello World!"
actual := make(chan string)
client1 := newClient(false)
client2 := newClient(false)
client1.AddCallback("JOIN", func(e *irc.Event) {
client1.Privmsg(e.Arguments[0], expected)
})
client2.AddCallback("JOIN", func(e *irc.Event) {
client2.Privmsg(e.Arguments[0], expected)
})
client1.AddCallback("PRIVMSG", func(e *irc.Event) {
actual <- e.Message()
})
client2.AddCallback("PRIVMSG", func(e *irc.Event) {
actual <- e.Message()
})
defer client1.Quit()
defer client2.Quit()
go client1.Loop()
go client2.Loop()
client1.Join("#channelprivmsg")
client2.Join("#channelprivmsg")
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
func TestChannel_NoExternal(t *testing.T) {
assert := assert.New(t)
expected := true
actual := make(chan bool)
client1 := newClient(true)
client2 := newClient(true)
client1.AddCallback("JOIN", func(e *irc.Event) {
channel := e.Arguments[0]
if channel == "#noexternal" {
if e.Nick == client1.GetNick() {
client2.Privmsg("#noexternal", "FooBar!")
} else {
assert.Fail(fmt.Sprintf("unexpected user %s joined %s", e.Nick, channel))
}
} else {
assert.Fail(fmt.Sprintf("unexpected channel %s", channel))
}
})
client2.AddCallback("PRIVMSG", func(e *irc.Event) {
if e.Arguments[0] == "#noexternal" {
actual <- false
}
})
client2.AddCallback("404", func(e *irc.Event) {
actual <- true
})
defer client1.Quit()
defer client2.Quit()
go client1.Loop()
go client2.Loop()
client1.Join("#noexternal")
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
func TestChannel_SetTopic_InvalidChannel(t *testing.T) {
assert := assert.New(t)
expected := true
actual := make(chan bool)
client1 := newClient(false)
client1.AddCallback("403", func(e *irc.Event) {
actual <- true
})
defer client1.Quit()
go client1.Loop()
client1.SendRaw("TOPIC #invalidchannel :FooBar")
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
func TestChannel_SetTopic_NotOnChannel(t *testing.T) {
assert := assert.New(t)
expected := true
actual := make(chan bool)
client1 := newClient(false)
client2 := newClient(false)
client1.AddCallback("442", func(e *irc.Event) {
actual <- true
})
client2.AddCallback("JOIN", func(e *irc.Event) {
client1.SendRaw("TOPIC #notonchannel :FooBar")
})
defer client1.Quit()
go client1.Loop()
client2.Join("#notonchannel")
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
func TestChannel_BadChannelKey(t *testing.T) {
assert := assert.New(t)
expected := true
actual := make(chan bool)
client1 := newClient(false)
client2 := newClient(false)
client1.AddCallback("324", func(e *irc.Event) {
if strings.Contains(e.Arguments[2], "k") {
client2.Join(e.Arguments[1])
} else {
client1.Mode("#badchannelkey")
}
})
client2.AddCallback("JOIN", func(e *irc.Event) {
if e.Nick == client2.GetNick() && e.Arguments[0] == "#badchannelkey" {
actual <- false
}
})
client2.AddCallback("475", func(e *irc.Event) {
actual <- true
})
defer client1.Quit()
defer client2.Quit()
go client1.Loop()
go client2.Loop()
client1.Join("#badchannelkey")
client1.Mode("#badchannelkey", "+k", "opensesame")
client1.Mode("#badchannelkey")
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
}
func TestChannel_GoodChannelKey(t *testing.T) {
assert := assert.New(t)
expected := true
actual := make(chan bool)
client1 := newClient(true)
client2 := newClient(true)
client1.AddCallback("324", func(e *irc.Event) {
if strings.Contains(e.Arguments[2], "k") {
client2.SendRawf("JOIN %s :opensesame", e.Arguments[1])
} else {
client1.Mode("#goodchannelkey")
}
})
client2.AddCallback("JOIN", func(e *irc.Event) {
if e.Nick == client2.GetNick() && e.Arguments[0] == "#goodchannelkey" {
actual <- true
}
})
client2.AddCallback("475", func(e *irc.Event) {
actual <- false
})
defer client1.Quit()
defer client2.Quit()
go client1.Loop()
go client2.Loop()
client1.Join("#goodchannelkey")
client1.Mode("#goodchannelkey", "+k", "opensesame")
client1.Mode("#goodchannelkey")
select {
case res := <-actual:
assert.Equal(expected, res)
case <-time.After(TIMEOUT):
assert.Fail("timeout")
}
assert.Equal(expected, <-actual)
}

1
vendor/github.com/google/uuid generated vendored

Submodule vendor/github.com/google/uuid deleted from dec09d789f

1
vendor/github.com/petermattis/goid generated vendored Submodule