/Godeps/_workspace/src/github.com/oschwald/maxminddb-golang/reader_test.go

https://gitlab.com/voxxit/gogeoip2 · Go · 398 lines · 335 code · 63 blank · 0 comment · 61 complexity · a859b4399e8c4c63ea4f8e720f119cf7 MD5 · raw file

  1. package maxminddb
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/ioutil"
  6. "math/big"
  7. "math/rand"
  8. "net"
  9. "testing"
  10. "time"
  11. . "gopkg.in/check.v1"
  12. )
  13. func TestMaxMindDbReader(t *testing.T) { TestingT(t) }
  14. type MySuite struct{}
  15. var _ = Suite(&MySuite{})
  16. func (s *MySuite) TestReader(c *C) {
  17. for _, recordSize := range []uint{24, 28, 32} {
  18. for _, ipVersion := range []uint{4, 6} {
  19. fileName := fmt.Sprintf("test-data/test-data/MaxMind-DB-test-ipv%d-%d.mmdb", ipVersion, recordSize)
  20. reader, err := Open(fileName)
  21. if err != nil {
  22. c.Logf("unexpected error while opening database: %v", err)
  23. c.Fail()
  24. }
  25. checkMetadata(c, reader, ipVersion, recordSize)
  26. if ipVersion == 4 {
  27. checkIpv4(c, reader)
  28. } else {
  29. checkIpv6(c, reader)
  30. }
  31. }
  32. }
  33. }
  34. func (s *MySuite) TestReaderBytes(c *C) {
  35. for _, recordSize := range []uint{24, 28, 32} {
  36. for _, ipVersion := range []uint{4, 6} {
  37. fileName := fmt.Sprintf("test-data/test-data/MaxMind-DB-test-ipv%d-%d.mmdb", ipVersion, recordSize)
  38. bytes, _ := ioutil.ReadFile(fileName)
  39. reader, err := FromBytes(bytes)
  40. if err != nil {
  41. c.Logf("unexpected error while opening bytes: %v", err)
  42. c.Fail()
  43. }
  44. checkMetadata(c, reader, ipVersion, recordSize)
  45. if ipVersion == 4 {
  46. checkIpv4(c, reader)
  47. } else {
  48. checkIpv6(c, reader)
  49. }
  50. }
  51. }
  52. }
  53. func (s *MySuite) TestDecodingToInterface(c *C) {
  54. reader, err := Open("test-data/test-data/MaxMind-DB-test-decoder.mmdb")
  55. if err != nil {
  56. c.Logf("unexpected error while opening database: %v", err)
  57. c.Fail()
  58. }
  59. var recordInterface interface{}
  60. err = reader.Lookup(net.ParseIP("::1.1.1.0"), &recordInterface)
  61. if err != nil {
  62. c.Logf("unexpected error while doing lookup: %v", err)
  63. c.Fail()
  64. }
  65. record := recordInterface.(map[string]interface{})
  66. c.Assert(record["array"], DeepEquals, []interface{}{uint64(1), uint64(2), uint64(3)})
  67. c.Assert(record["boolean"], Equals, true)
  68. c.Assert(record["bytes"], DeepEquals, []byte{0x00, 0x00, 0x00, 0x2a})
  69. c.Assert(record["double"], Equals, 42.123456)
  70. c.Assert(record["float"], Equals, float32(1.1))
  71. c.Assert(record["int32"], Equals, -268435456)
  72. c.Assert(record["map"], DeepEquals,
  73. map[string]interface{}{
  74. "mapX": map[string]interface{}{
  75. "arrayX": []interface{}{uint64(7), uint64(8), uint64(9)},
  76. "utf8_stringX": "hello",
  77. }})
  78. c.Assert(record["uint16"], Equals, uint64(100))
  79. c.Assert(record["uint32"], Equals, uint64(268435456))
  80. c.Assert(record["uint64"], Equals, uint64(1152921504606846976))
  81. c.Assert(record["utf8_string"], Equals, "unicode! ☯ - ♫")
  82. bigInt := new(big.Int)
  83. bigInt.SetString("1329227995784915872903807060280344576", 10)
  84. c.Assert(record["uint128"], DeepEquals, bigInt)
  85. }
  86. type TestType struct {
  87. Array []uint `maxminddb:"array"`
  88. Boolean bool `maxminddb:"boolean"`
  89. Bytes []byte `maxminddb:"bytes"`
  90. Double float64 `maxminddb:"double"`
  91. Float float32 `maxminddb:"float"`
  92. Int32 int32 `maxminddb:"int32"`
  93. Map map[string]interface{} `maxminddb:"map"`
  94. Uint16 uint16 `maxminddb:"uint16"`
  95. Uint32 uint32 `maxminddb:"uint32"`
  96. Uint64 uint64 `maxminddb:"uint64"`
  97. Uint128 big.Int `maxminddb:"uint128"`
  98. Utf8String string `maxminddb:"utf8_string"`
  99. }
  100. func (s *MySuite) TestDecoder(c *C) {
  101. reader, err := Open("test-data/test-data/MaxMind-DB-test-decoder.mmdb")
  102. if err != nil {
  103. c.Logf("unexpected error while opening database: %v", err)
  104. c.Fail()
  105. }
  106. var result TestType
  107. err = reader.Lookup(net.ParseIP("::1.1.1.0"), &result)
  108. if err != nil {
  109. c.Log(err)
  110. c.Fail()
  111. }
  112. c.Assert(result.Array, DeepEquals, []uint{uint(1), uint(2), uint(3)})
  113. c.Assert(result.Boolean, Equals, true)
  114. c.Assert(result.Bytes, DeepEquals, []byte{0x00, 0x00, 0x00, 0x2a})
  115. c.Assert(result.Double, Equals, 42.123456)
  116. c.Assert(result.Float, Equals, float32(1.1))
  117. c.Assert(result.Int32, Equals, int32(-268435456))
  118. c.Assert(result.Map, DeepEquals,
  119. map[string]interface{}{
  120. "mapX": map[string]interface{}{
  121. "arrayX": []interface{}{uint64(7), uint64(8), uint64(9)},
  122. "utf8_stringX": "hello",
  123. }})
  124. c.Assert(result.Uint16, Equals, uint16(100))
  125. c.Assert(result.Uint32, Equals, uint32(268435456))
  126. c.Assert(result.Uint64, Equals, uint64(1152921504606846976))
  127. c.Assert(result.Utf8String, Equals, "unicode! ☯ - ♫")
  128. bigInt := new(big.Int)
  129. bigInt.SetString("1329227995784915872903807060280344576", 10)
  130. c.Assert(&result.Uint128, DeepEquals, bigInt)
  131. reader.Close()
  132. }
  133. func (s *MySuite) TestIpv6inIpv4(c *C) {
  134. reader, err := Open("test-data/test-data/MaxMind-DB-test-ipv4-24.mmdb")
  135. if err != nil {
  136. c.Logf("unexpected error while opening database: %v", err)
  137. c.Fail()
  138. }
  139. var result TestType
  140. err = reader.Lookup(net.ParseIP("2001::"), &result)
  141. var emptyResult TestType
  142. c.Assert(result, DeepEquals, emptyResult)
  143. expected := errors.New("error looking up '2001::': you attempted to look up an IPv6 address in an IPv4-only database")
  144. c.Assert(err, DeepEquals, expected)
  145. reader.Close()
  146. }
  147. func (s *MySuite) TestBrokenDatabase(c *C) {
  148. reader, err := Open("test-data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb")
  149. if err != nil {
  150. c.Logf("unexpected error while opening database: %v", err)
  151. c.Fail()
  152. }
  153. var result interface{}
  154. err = reader.Lookup(net.ParseIP("2001:220::"), &result)
  155. expected := errors.New("the MaxMind DB file's data section contains bad data (float 64 size of 2)")
  156. c.Assert(err, DeepEquals, expected)
  157. reader.Close()
  158. }
  159. func (s *MySuite) TestMissingDatabase(c *C) {
  160. reader, err := Open("file-does-not-exist.mmdb")
  161. if reader != nil {
  162. c.Log("received reader when doing lookups on DB that doesn't exist")
  163. c.Fail()
  164. }
  165. c.Assert(err, ErrorMatches, "open file-does-not-exist.mmdb.*")
  166. }
  167. func (s *MySuite) TestNonDatabase(c *C) {
  168. reader, err := Open("README.md")
  169. if reader != nil {
  170. c.Log("received reader when doing lookups on DB that doesn't exist")
  171. c.Fail()
  172. }
  173. c.Assert(err.Error(), Equals, "error opening database file: invalid MaxMind DB file")
  174. }
  175. func (s *MySuite) TestDecodingToNonPointer(c *C) {
  176. reader, _ := Open("test-data/test-data/MaxMind-DB-test-decoder.mmdb")
  177. var recordInterface interface{}
  178. err := reader.Lookup(net.ParseIP("::1.1.1.0"), recordInterface)
  179. c.Assert(err.Error(), Equals, "result param must be a pointer")
  180. reader.Close()
  181. }
  182. func (s *MySuite) TestNilLookup(c *C) {
  183. reader, _ := Open("test-data/test-data/MaxMind-DB-test-decoder.mmdb")
  184. var recordInterface interface{}
  185. err := reader.Lookup(nil, recordInterface)
  186. c.Assert(err.Error(), Equals, "ipAddress passed to Lookup cannot be nil")
  187. reader.Close()
  188. }
  189. func checkMetadata(c *C, reader *Reader, ipVersion uint, recordSize uint) {
  190. metadata := reader.Metadata
  191. c.Assert(metadata.BinaryFormatMajorVersion, Equals, uint(2))
  192. c.Assert(metadata.BinaryFormatMinorVersion, Equals, uint(0))
  193. c.Assert(metadata.BuildEpoch, FitsTypeOf, uint(0))
  194. c.Assert(metadata.DatabaseType, Equals, "Test")
  195. c.Assert(metadata.Description, DeepEquals,
  196. map[string]string{
  197. "en": "Test Database",
  198. "zh": "Test Database Chinese",
  199. })
  200. c.Assert(metadata.IPVersion, Equals, ipVersion)
  201. c.Assert(metadata.Languages, DeepEquals, []string{"en", "zh"})
  202. if ipVersion == 4 {
  203. c.Assert(metadata.NodeCount, Equals, uint(37))
  204. } else {
  205. c.Assert(metadata.NodeCount, Equals, uint(160))
  206. }
  207. c.Assert(metadata.RecordSize, Equals, recordSize)
  208. }
  209. func checkIpv4(c *C, reader *Reader) {
  210. for i := uint(0); i < 6; i++ {
  211. address := fmt.Sprintf("1.1.1.%d", uint(1)<<i)
  212. ip := net.ParseIP(address)
  213. var result map[string]string
  214. err := reader.Lookup(ip, &result)
  215. if err != nil {
  216. c.Logf("unexpected error while doing lookup: %v", err)
  217. c.Fail()
  218. }
  219. c.Assert(result, DeepEquals, map[string]string{
  220. "ip": address})
  221. }
  222. pairs := map[string]string{
  223. "1.1.1.3": "1.1.1.2",
  224. "1.1.1.5": "1.1.1.4",
  225. "1.1.1.7": "1.1.1.4",
  226. "1.1.1.9": "1.1.1.8",
  227. "1.1.1.15": "1.1.1.8",
  228. "1.1.1.17": "1.1.1.16",
  229. "1.1.1.31": "1.1.1.16",
  230. }
  231. for keyAddress, valueAddress := range pairs {
  232. data := map[string]string{"ip": valueAddress}
  233. ip := net.ParseIP(keyAddress)
  234. var result map[string]string
  235. err := reader.Lookup(ip, &result)
  236. if err != nil {
  237. c.Logf("unexpected error while doing lookup: %v", err)
  238. c.Fail()
  239. }
  240. c.Assert(result, DeepEquals, data)
  241. }
  242. for _, address := range []string{"1.1.1.33", "255.254.253.123"} {
  243. ip := net.ParseIP(address)
  244. var result map[string]string
  245. err := reader.Lookup(ip, &result)
  246. if err != nil {
  247. c.Logf("unexpected error while doing lookup: %v", err)
  248. c.Fail()
  249. }
  250. c.Assert(result, IsNil)
  251. }
  252. }
  253. func checkIpv6(c *C, reader *Reader) {
  254. subnets := []string{"::1:ffff:ffff", "::2:0:0",
  255. "::2:0:40", "::2:0:50", "::2:0:58"}
  256. for _, address := range subnets {
  257. var result map[string]string
  258. err := reader.Lookup(net.ParseIP(address), &result)
  259. if err != nil {
  260. c.Logf("unexpected error while doing lookup: %v", err)
  261. c.Fail()
  262. }
  263. c.Assert(result, DeepEquals, map[string]string{"ip": address})
  264. }
  265. pairs := map[string]string{
  266. "::2:0:1": "::2:0:0",
  267. "::2:0:33": "::2:0:0",
  268. "::2:0:39": "::2:0:0",
  269. "::2:0:41": "::2:0:40",
  270. "::2:0:49": "::2:0:40",
  271. "::2:0:52": "::2:0:50",
  272. "::2:0:57": "::2:0:50",
  273. "::2:0:59": "::2:0:58",
  274. }
  275. for keyAddress, valueAddress := range pairs {
  276. data := map[string]string{"ip": valueAddress}
  277. var result map[string]string
  278. err := reader.Lookup(net.ParseIP(keyAddress), &result)
  279. if err != nil {
  280. c.Logf("unexpected error while doing lookup: %v", err)
  281. c.Fail()
  282. }
  283. c.Assert(result, DeepEquals, data)
  284. }
  285. for _, address := range []string{"1.1.1.33", "255.254.253.123", "89fa::"} {
  286. var result map[string]string
  287. err := reader.Lookup(net.ParseIP(address), &result)
  288. if err != nil {
  289. c.Logf("unexpected error while doing lookup: %v", err)
  290. c.Fail()
  291. }
  292. c.Assert(result, IsNil)
  293. }
  294. }
  295. func BenchmarkMaxMindDB(b *testing.B) {
  296. db, err := Open("GeoLite2-City.mmdb")
  297. if err != nil {
  298. b.Fatal(err)
  299. }
  300. r := rand.New(rand.NewSource(time.Now().UnixNano()))
  301. var result interface{}
  302. for i := 0; i < b.N; i++ {
  303. num := r.Uint32()
  304. ip := net.ParseIP(fmt.Sprintf("%d.%d.%d.%d", (0xFF000000&num)>>24,
  305. (0x00FF0000&num)>>16, (0x0000FF00&num)>>8, 0x000000FF&num))
  306. err := db.Lookup(ip, &result)
  307. if err != nil {
  308. b.Fatal(err)
  309. }
  310. }
  311. db.Close()
  312. }
  313. func BenchmarkCountryCode(b *testing.B) {
  314. db, err := Open("GeoLite2-City.mmdb")
  315. if err != nil {
  316. b.Fatal(err)
  317. }
  318. type MinCountry struct {
  319. Country struct {
  320. IsoCode string `maxminddb:"iso_code"`
  321. } `maxminddb:"country"`
  322. }
  323. r := rand.New(rand.NewSource(time.Now().UnixNano()))
  324. var result MinCountry
  325. for i := 0; i < b.N; i++ {
  326. num := r.Uint32()
  327. ip := net.ParseIP(fmt.Sprintf("%d.%d.%d.%d", (0xFF000000&num)>>24,
  328. (0x00FF0000&num)>>16, (0x0000FF00&num)>>8, 0x000000FF&num))
  329. err := db.Lookup(ip, &result)
  330. if err != nil {
  331. b.Fatal(err)
  332. }
  333. }
  334. db.Close()
  335. }