all repos

rss-tools @ master

get rss feed from sources that(i need and) dont provide one

rss-tools/sources/ztoe/ztoe_test.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
refactor atom feed builder, 14 days ago
1
package ztoe
2
3
import (
4
	"bytes"
5
	"context"
6
	"encoding/xml"
7
	"net/http"
8
	"net/http/httptest"
9
	"os"
10
	"path/filepath"
11
	"strings"
12
	"testing"
13
14
	"olexsmir.xyz/rss-tools/app/atom"
15
	"olexsmir.xyz/x/is"
16
)
17
18
func TestParseScheduleFromSampleFixture(t *testing.T) {
19
	html := mustReadFixture(t, "ztoe-unclean.html")
20
	schedule, err := parseSchedule(bytes.NewReader(html), "text/html; charset=windows-1251")
21
22
	is.Err(t, err, nil)
23
	is.Equal(t, len(schedule.TimeSlots), 48)
24
	is.Equal(t, schedule.Date, "10.04.2026")
25
	is.NotEqual(t, len(schedule.Rows), 0)
26
	is.NotEqual(t, countOutages(schedule.Rows), 0)
27
}
28
29
func TestParseScheduleFromClenFixture(t *testing.T) {
30
	html := mustReadFixture(t, "ztoe-clean.html")
31
	schedule, err := parseSchedule(bytes.NewReader(html), "text/html")
32
33
	is.Err(t, err, nil)
34
	is.Equal(t, len(schedule.TimeSlots), 48)
35
	is.Equal(t, countOutages(schedule.Rows), 0)
36
}
37
38
func TestHandlerRendersAtomFeedWithOutages(t *testing.T) {
39
	html := mustReadFixture(t, "ztoe-unclean.html")
40
	schedule, err := parseSchedule(bytes.NewReader(html), "text/html; charset=windows-1251")
41
	is.Err(t, err, nil)
42
43
	subgroup, ok := subgroupWithOutage(schedule.Rows)
44
	is.Equal(t, ok, true)
45
46
	group, subgroupPart, splitOK := strings.Cut(subgroup, ".")
47
	is.Equal(t, splitOK, true)
48
49
	remote := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
50
		w.Header().Set("Content-Type", "text/html; charset=windows-1251")
51
		_, _ = w.Write(html)
52
	}))
53
	defer remote.Close()
54
55
	z := ztoe{get: func(ctx context.Context, url string) (*http.Response, error) {
56
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
57
		is.Err(t, err, nil)
58
		return remote.Client().Do(req)
59
	}}
60
61
	mux := http.NewServeMux()
62
	mux.HandleFunc("GET /ztoe/{group}/{subgroup}", z.handler(remote.URL))
63
64
	req := httptest.NewRequest(http.MethodGet, "/ztoe/"+group+"/"+subgroupPart, nil)
65
	rr := httptest.NewRecorder()
66
	mux.ServeHTTP(rr, req)
67
68
	is.Equal(t, http.StatusOK, rr.Code)
69
	if got := rr.Header().Get("Content-Type"); !strings.Contains(got, "application/atom+xml") {
70
		t.Fatalf("expected atom response content-type, got %q", got)
71
	}
72
73
	var feed atom.Feed
74
	is.Err(t, xml.NewDecoder(rr.Body).Decode(&feed), nil)
75
	is.NotEqual(t, 0, len(feed.Entry))
76
}
77
78
func TestHandlerRendersEmptyAtomFeedForNoOutages(t *testing.T) {
79
	html := mustReadFixture(t, "ztoe-clean.html")
80
	remote := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
81
		w.Header().Set("Content-Type", "text/html")
82
		_, _ = w.Write(html)
83
	}))
84
	defer remote.Close()
85
86
	z := ztoe{get: func(ctx context.Context, url string) (*http.Response, error) {
87
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
88
		is.Err(t, err, nil)
89
		return remote.Client().Do(req)
90
	}}
91
92
	mux := http.NewServeMux()
93
	mux.HandleFunc("GET /ztoe/{group}/{subgroup}", z.handler(remote.URL))
94
95
	req := httptest.NewRequest(http.MethodGet, "/ztoe/1/1", nil)
96
	rr := httptest.NewRecorder()
97
	mux.ServeHTTP(rr, req)
98
99
	if rr.Code != http.StatusOK {
100
		t.Fatalf("expected status 200, got %d", rr.Code)
101
	}
102
103
	var feed atom.Feed
104
	is.Err(t, xml.NewDecoder(rr.Body).Decode(&feed), nil)
105
	is.Equal(t, 0, len(feed.Entry))
106
}
107
108
func mustReadFixture(t *testing.T, name string) []byte {
109
	t.Helper()
110
	path := filepath.Join("testdata", name)
111
	data, err := os.ReadFile(path)
112
	is.Err(t, err, nil)
113
	return data
114
}
115
116
func countOutages(rows map[string][]bool) int {
117
	total := 0
118
	for _, slots := range rows {
119
		for _, outage := range slots {
120
			if outage {
121
				total++
122
			}
123
		}
124
	}
125
	return total
126
}
127
128
func subgroupWithOutage(rows map[string][]bool) (string, bool) {
129
	for subgroup, slots := range rows {
130
		for _, outage := range slots {
131
			if outage {
132
				return subgroup, true
133
			}
134
		}
135
	}
136
	return "", false
137
}