Index: geniuswebcore/test/geniusweb/issuevalue/BidTest.py
===================================================================
--- geniuswebcore/test/geniusweb/issuevalue/BidTest.py	(revision 66)
+++ geniuswebcore/test/geniusweb/issuevalue/BidTest.py	(revision 67)
@@ -1,45 +1,126 @@
+from collections import OrderedDict
 from decimal import Decimal
+import json
+from typing import List, Dict
 import unittest
+from unittest.mock import Mock
 
 from pyson.ObjectMapper import ObjectMapper
+from unitpy.GeneralTests import GeneralTests
 
-from geniusweb.actions.PartyId import PartyId
 from geniusweb.issuevalue.Bid import Bid
 from geniusweb.issuevalue.DiscreteValue import DiscreteValue
 from geniusweb.issuevalue.NumberValue import NumberValue
+from geniusweb.issuevalue.Value import Value
 
 
-class BidTest (unittest.TestCase) :
-    pyson=ObjectMapper()
-    actor=PartyId("myid")
-    bid=Bid({'fte':NumberValue(Decimal(3)), 'leasecar':DiscreteValue('yes')})
-    bid2=Bid({'fte':NumberValue(Decimal(3)), 'leasecar':DiscreteValue('no')})
-    bid3=Bid({'fte':NumberValue(Decimal(-3)), 'leasecar':DiscreteValue('yes')})
-    
-    def testBadValue(self):
-        self.assertRaises(ValueError, lambda:Bid({"iss":3}))
+class BidTest (unittest.TestCase,  GeneralTests[Bid]):
 
-    def testSerialize(self):
-        print(str(self.pyson.toJson(self.bid)))
-        self.assertEqual({'issuevalues':{'fte': 3, 'leasecar': 'yes'}},\
-                self.pyson.toJson(self.bid))
-        self.assertEqual({'issuevalues':{'fte': -3, 'leasecar': 'yes'}},\
-                self.pyson.toJson(self.bid3))
-        
-    def testDeserialize(self):
-        jsonoffer= self.pyson.toJson(self.bid)
-        self.assertEqual(self.bid, self.pyson.parse(jsonoffer, Bid))
-    
-    def testNonValue(self):
-        self.assertRaises(ValueError, lambda: Bid({"a":12}))
-        
-    
-    def testRepr(self):
-        self.assertEqual("Bid{'fte': 3, 'leasecar': yes}", repr(self.bid))
-        
-    def testEqual(self): 
-        self.assertEqual(self.bid, self.bid)
-        self.assertNotEqual(self.bid, self.bid2)
-        self.assertEqual(hash(self.bid), hash(self.bid))        
-        self.assertNotEqual(hash(self.bid), hash(self.bid2))
-        
+    asString1 = "{\"issuevalues\":{\"issue3\":901234567.89,\"issue2\":1,\"issue1\":\"b\"}}"
+    ISSUE1 = "issue1"
+    VALUE1:Value = DiscreteValue("value1")
+    ISSUE2 = "issue2"
+    VALUE2:Value  =  NumberValue(Decimal("10"))
+    list:List[List[Bid]] = []
+
+    issuevalues:Dict[str, Value]  = OrderedDict()
+    issuevalues["issue3"]=  NumberValue(Decimal("901234567.89"))
+    issuevalues[ISSUE2]= NumberValue(Decimal(1))
+    issuevalues[ISSUE1]= DiscreteValue("b")
+    bid = Bid(issuevalues);
+
+    # different order but that shouldn't matter
+    issuevalues1:Dict[str, Value]  = OrderedDict()
+    issuevalues1["issue3"]= NumberValue(Decimal("901234567.89"))
+    issuevalues1[ISSUE2]= NumberValue(Decimal(1))
+    issuevalues1[ISSUE1]= DiscreteValue("b")
+    bid1 = Bid(issuevalues1)
+
+    issuevaluesb:Dict[str, Value] = OrderedDict()
+    issuevaluesb[ISSUE2]= NumberValue(Decimal(1))
+    issuevaluesb[ISSUE1]= DiscreteValue("b")
+    bidb = Bid(issuevaluesb)
+
+    # bidc and bidd have values swapped, so different issuevalues.
+    issuevaluesc:Dict[str, Value]  = OrderedDict()
+    issuevaluesc[ISSUE2]=VALUE2
+    issuevaluesc[ISSUE1]= VALUE1
+    bidc =  Bid(issuevaluesc)
+
+    list.append([bid, bid1])
+    list.append([bidb])
+    list.append([bidc])
+   
+    def getGeneralTestData(self)->List[List[Bid]] :
+        return self.list
+
+    def getGeneralTestStrings(self)->List[str] : 
+        return [
+                "Bid\\{issue3=901234567.89, issue2=1, issue1=\"b\"\\}",
+                "Bid\\{issue2=1, issue1=\"b\"\\}",
+                "Bid\\{issue2=10, issue1=\"value1\"\\}"]
+
+
+    def testNullConstructor(self):
+        issuevalues = None;
+        self.assertRaises(ValueError, lambda: Bid(issuevalues))
+
+    def testBidSimple(self):
+        issuevalues = {}
+        Bid(issuevalues) # no issues, no values should be ok
+
+    def testBidNull(self):
+        issuevalues = {}
+        issuevalues[self.ISSUE1]=None
+        self.assertRaises(ValueError, lambda:Bid(issuevalues)) # smokes
+
+    def testbidOkIssueValue(self):
+        issuevalues = OrderedDict()
+        issuevalues[self.ISSUE1]=Mock(Value)
+        Bid(issuevalues) # shouldn't smoke
+
+    def testSerialize(self) :
+        jackson = ObjectMapper()
+        print(str(jackson.toJson(self.bid)))
+        self.assertEqual(json.loads(self.asString1), jackson.toJson(self.bid))
+
+    def testDeserialize(self) :
+        jackson = ObjectMapper()
+        read = jackson.parse(json.loads(self.asString1), Bid)
+        self.assertEquals(self.bid, read)
+
+    def testMerge(self):
+        issuevalues = {}
+        issuevalues[self.ISSUE1]= self.VALUE1
+        partial1 = Bid(issuevalues)
+
+        issuevalues = {}
+        issuevalues[self.ISSUE2]= self.VALUE2
+        partial2 = Bid(issuevalues)
+
+        mergedbid = partial1.merge(partial2)
+        self.assertEqual(self.bidc, mergedbid)
+
+    def testMergeIssuesOverlap(self):
+        issuevalues = {}
+        issuevalues[self.ISSUE1]=self.VALUE1
+        partial1 = Bid(issuevalues)
+
+        issuevalues = {}
+        issuevalues[self.ISSUE1]=self.VALUE2
+        partial2 =  Bid(issuevalues)
+
+        self.assertRaises(ValueError, lambda:partial1.merge(partial2))
+
+    def testSmokeConstructor2(self):
+        Bid({"issue": DiscreteValue("dss")})
+
+    def testSmokeConstructor2b(self):
+        Bid({"issue": NumberValue(Decimal("0.5"))})
+
+    def testSmokeConstructor2Null1(self):
+        self.assertRaises(ValueError, lambda:Bid({None: DiscreteValue("dss")}))
+
+    def testSmokeConstructor2Null2(self):
+        self.assertRaises(ValueError, lambda: Bid({"issue": None}))
+
Index: geniuswebcore/test/geniusweb/issuevalue/DiscreteValueSetTest.py
===================================================================
--- geniuswebcore/test/geniusweb/issuevalue/DiscreteValueSetTest.py	(revision 66)
+++ geniuswebcore/test/geniusweb/issuevalue/DiscreteValueSetTest.py	(revision 67)
@@ -1,2 +1,4 @@
+import json
+from typing import List
 import unittest
 
@@ -9,29 +11,76 @@
 
 class DiscreteValueSetTest(unittest.TestCase):
-    pyson=ObjectMapper()
-    val = DiscreteValueSet([DiscreteValue("a"), DiscreteValue("b")])
-    val1 = DiscreteValueSet([DiscreteValue("a"), DiscreteValue("b")])
-    val2 = DiscreteValueSet([DiscreteValue("a"), DiscreteValue("c")])
-    valjson = {"values":["a","b"]}
 
-    def testSerialize(self):
-        print(str(self.pyson.toJson(self.val)))
-        self.assertEqual(self.valjson, self.pyson.toJson(self.val))
-        
+    A = DiscreteValue("a");
+    A1 = DiscreteValue("a");
+    B = DiscreteValue("b");
+    values:List[DiscreteValue]  = [A, B]
+    valuesReverse = [B, A]
+
+    set1 = DiscreteValueSet(values)
+    setReverse = DiscreteValueSet(valuesReverse)
+    asString = "{\"values\":[\"a\",\"b\"]}"
+    # String asString = "{\"DiscreteValueSet
+    # \":[\"a\",\"b\"]}";
+    asString1 = "[\"a\",\"b\"]"
+
+    # SPECIAL. note that the "numbers" have no leading "=" and therefore are
+    # parsed as discrete values.
+    discretenumbers = "{\"values\":[\"0\",\"1\"]}"
+    jackson = ObjectMapper()
+
+    def  testSerialize(self) :
+        jsonobj = self.jackson.toJson(self.set1)
+        print(jsonobj)
+        self.assertEqual(json.loads(self.asString), jsonobj)
+
     def testDeserialize(self):
-        self.assertEqual(self.val, self.pyson.parse(self.valjson, ValueSet))
-        
-        
-    def testIterator(self):
-        self.assertTrue(DiscreteValue("a") in self.val)
-        
-            
-    def testRepr(self):
-        self.assertEqual("DiscreteValueSet[a, b]", repr(self.val))
-        
-    def testEqual(self):
-        self.assertEqual(self.val, self.val1)
-        self.assertNotEqual(self.val, self.val2)
-        self.assertEqual(hash(self.val), hash(self.val1))
-        self.assertNotEqual(hash(self.val), hash(self.val2))
-        
+        p = self.jackson.parse(json.loads(self.asString), DiscreteValueSet)
+        print(p)
+        self.assertEqual(self.set1, p)
+
+    def testDeserializeNumberValues(self):
+        p = self.jackson.parse(json.loads(self.discretenumbers), DiscreteValueSet)
+        print(p)
+        self.assertEqual("0", p.get(0).getValue()) # not the number but string
+        self.assertEquals("1", p.get(1).getValue())
+
+    def testEquality(self):
+        self.assertEqual(self.set1, self.setReverse)
+
+    def testHashCodeInsensitiveToOrder(self):
+        self.assertEqual(hash(self.set1), hash(self.setReverse))
+
+    def testSize(self):
+        self.assertEqual(2, self.set1.size())
+
+    def testGet(self): 
+        self.assertEqual(self.A, self.set1.get(0))
+        self.assertEqual(self.B, self.set1.get(1))
+        self.assertEqual(self.A, self.set1.get(0))
+        self.assertEqual(self.B, self.set1.get(1))
+
+    def testgetValues(self):
+        self.assertEqual(self.values, self.set1.getValues())
+
+    def testConstructor2(self):
+        self.assertEqual(self.set1, DiscreteValueSet([self.A, self.B]))
+
+    def testContains(self):
+        self.assertTrue(self.set1.contains(self.A))
+        self.assertTrue(self.set1.contains(self.A1))
+        self.assertTrue(self.set1.contains(self.B))
+        self.assertFalse(self.set1.contains(DiscreteValue("c")))
+        self.assertFalse(self.set1.contains(None))
+
+    def testiterator1(self) :
+        it = iter(self.set1);
+        self.assertEqual(self.A, next(it));
+        self.assertEqual(self.B, next(it));
+        self.assertRaises(StopIteration, lambda: next(it))
+
+    def testiterator2(self): 
+        it = iter(self.setReverse)
+        self.assertEqual(self.B, next(it))
+        self.assertEqual(self.A, next(it));
+        self.assertRaises(StopIteration,lambda: next(it))
Index: geniuswebcore/test/geniusweb/issuevalue/DiscreteValueTest.py
===================================================================
--- geniuswebcore/test/geniusweb/issuevalue/DiscreteValueTest.py	(revision 66)
+++ geniuswebcore/test/geniusweb/issuevalue/DiscreteValueTest.py	(revision 67)
@@ -1,5 +1,9 @@
+import json
+from pickle import NONE
+from typing import List
 import unittest
 
 from pyson.ObjectMapper import ObjectMapper
+from unitpy.GeneralTests import GeneralTests
 
 from geniusweb.issuevalue.DiscreteValue import DiscreteValue
@@ -7,25 +11,26 @@
 
 
-class DiscreteValueTest(unittest.TestCase):
-    pyson=ObjectMapper()
-    val = DiscreteValue('val')
-    val1 = DiscreteValue('val')
-    val2 = DiscreteValue('val2')
-    valjson = 'val'
+class DiscreteValueTest(unittest.TestCase, GeneralTests[DiscreteValue]):
+    value =  DiscreteValue("a");
+    value1 =  DiscreteValue("a");
+    valueb =  DiscreteValue("b");
+    serialized = "\"a\"";
+    jackson =  ObjectMapper();
 
     def testSerialize(self):
-        print(str(self.pyson.toJson(self.val)))
-        self.assertEqual(self.valjson, self.pyson.toJson(self.val))
+        print(str(self.jackson.toJson(self.value)))
+        self.assertEqual(json.loads(self.serialized), self.jackson.toJson(self.value))
         
-    def testDeserialize(self):
-        self.assertEqual(self.val, self.pyson.parse(self.valjson, Value))
+    def testDeserialize(self) :
+        self.assertEqual(self.value, self.jackson.parse(json.loads(self.serialized), DiscreteValue))
+ 
+    def testDeserializeFromValue(self):
+        self.assertEqual(self.value, self.jackson.parse(json.loads(self.serialized), Value))
         
-    def testRepr(self):
-        self.assertEqual('val', repr(self.val))
-        
-    def testEqual(self):
-        self.assertEqual(self.val, self.val1)
-        self.assertNotEqual(self.val, self.val2)
-        self.assertEqual(hash(self.val), hash(self.val1))
-        self.assertNotEqual(hash(self.val), hash(self.val2))
-        
+
+    def getGeneralTestData(self)->List[List[DiscreteValue]] :
+        return [ [self.value, self.value1], [self.valueb]]
+
+    def getGeneralTestStrings(self)->List[str]:
+        return [self.serialized, "\"b\""]
+    
Index: geniuswebcore/test/geniusweb/issuevalue/DomainTest.py
===================================================================
--- geniuswebcore/test/geniusweb/issuevalue/DomainTest.py	(revision 66)
+++ geniuswebcore/test/geniusweb/issuevalue/DomainTest.py	(revision 67)
@@ -1,2 +1,3 @@
+from decimal import Decimal
 import json
 import unittest
@@ -12,4 +13,5 @@
 
 class DomainTest (unittest.TestCase) :
+    maxDiff=None
 
     pyson=ObjectMapper()
@@ -55,11 +57,41 @@
         self.assertEqual(self.jobs, self.pyson.parse(self.jobsjson, Domain))
     
+    def testInit(self):
+        self.assertRaises(ValueError, lambda: Domain("name",None))
+        self.assertRaises(ValueError, lambda: Domain(None,{"issue"}))
+        self.assertRaises(ValueError, lambda: Domain("_invalid_name",{"issue"}))
+        self.assertRaises(ValueError, lambda: Domain("name",{}))
+        
     def testIsFitting(self):
         self.assertEqual(None,self.jobs.isFitting(Bid({"lease car":DiscreteValue("no")})))
-        self.assertTrue("illegal value maybe" in self.jobs.isFitting(Bid({"lease car":DiscreteValue("maybe")})))
+        self.assertTrue("refers to non-domain issue" in self.jobs.isFitting(Bid({"no such issue":DiscreteValue("no")})))
+        self.assertNotEqual(None,self.jobs.isFitting(Bid({"lease car":NumberValue(Decimal(1))})))
+        self.assertTrue('illegal value' in self.jobs.isFitting(Bid({"lease car":DiscreteValue("maybe")})))
+     
+    def testIsComplete(self):
+        self.assertEqual(None,self.jobs.isComplete(Bid({"lease car":DiscreteValue("no"),
+                                                        "permanent contract":DiscreteValue("yes"),
+                                                        "career development opportunities":DiscreteValue("high"),
+                                                        "fte":DiscreteValue("1.0"),
+                                                        "salary":DiscreteValue("3000"),
+                                                        "work from home":DiscreteValue("1")
+                                                        })))
+        self.assertTrue("do not match issues in domain" in self.jobs.isComplete(Bid({"lease car":DiscreteValue("no"),
+                                                        "permanent contract":DiscreteValue("yes"),
+                                                        "work from home":DiscreteValue("1")
+                                                        })))
         
+    def testGetValues(self):
+        self.assertEqual(self.jobs.getValues("lease car"),self.yesno)
+        
+    def testgetIssues(self):
+        self.assertEqual(self.jobs.getIssues(),{'permanent contract', 'fte', 'lease car', 'career development opportunities', 'salary', 'work from home'})
+               
+    def testgetName(self):
+        self.assertEqual(self.jobs.getName(),"jobs")
+
     def testRepr(self):
-        self.assertEqual("Domain[jobs,{'lease car': DiscreteValueSet[yes, no], 'permanent contract': DiscreteValueSet[yes, no], 'career development opportunities': DiscreteValueSet[low, medium, high], 'fte': DiscreteValueSet[0.6, 0.8, 1.0], 'salary': DiscreteValueSet[2000, 2500, 3000, 3500, 4000], 'work from home': DiscreteValueSet[0, 1, 2]}]", \
-                            repr(self.jobs))
+        self.assertEqual("Domain[jobs,{'lease car': DiscreteValueSet[\"yes\", \"no\"], 'permanent contract': DiscreteValueSet[\"yes\", \"no\"], 'career development opportunities': DiscreteValueSet[\"low\", \"medium\", \"high\"], 'fte': DiscreteValueSet[\"0.6\", \"0.8\", \"1.0\"], 'salary': DiscreteValueSet[\"2000\", \"2500\", \"3000\", \"3500\", \"4000\"], 'work from home': DiscreteValueSet[\"0\", \"1\", \"2\"]}]"\
+                         ,repr(self.jobs))
         
     def testEqual(self):
Index: geniuswebcore/test/geniusweb/issuevalue/NumberValueSetTest.py
===================================================================
--- geniuswebcore/test/geniusweb/issuevalue/NumberValueSetTest.py	(revision 66)
+++ geniuswebcore/test/geniusweb/issuevalue/NumberValueSetTest.py	(revision 67)
@@ -17,4 +17,7 @@
     valjson = {"range":{"low":12.2,"high":12.6,"step":0.3}}
 
+    def testInit(self):
+        self.assertRaises(ValueError, lambda: NumberValueSet(range(1,5)))
+        
     def testSerialize(self):
         print(str(self.pyson.toJson(self.val)))
Index: geniuswebcore/test/geniusweb/issuevalue/NumberValueTest.py
===================================================================
--- geniuswebcore/test/geniusweb/issuevalue/NumberValueTest.py	(revision 66)
+++ geniuswebcore/test/geniusweb/issuevalue/NumberValueTest.py	(revision 67)
@@ -1,6 +1,9 @@
 from _decimal import Decimal
+import json
+from typing import List, Any
 import unittest
 
 from pyson.ObjectMapper import ObjectMapper
+from unitpy.GeneralTests import GeneralTests
 
 from geniusweb.issuevalue.NumberValue import NumberValue
@@ -8,28 +11,71 @@
 
 
-class NumberValueTest(unittest.TestCase):
-    pyson=ObjectMapper()
-    val = NumberValue(Decimal('0.1237'))
-    val2 = NumberValue(Decimal('2'))
-    valjson = 0.1237
+class NumberValueTest(unittest.TestCase, GeneralTests[NumberValue]):
+    value =  NumberValue(Decimal("12.31"))
+    value1 =  NumberValue(Decimal("12.31"))
+    value1c = NumberValue(Decimal("1.231E+1"))
+    valueb =  NumberValue(Decimal("12.310000000000000001"))
+    serialized = "12.31"
 
-    def testSerialize(self):
-        print(str(self.pyson.toJson(self.val)))
-        self.assertEqual(self.valjson, self.pyson.toJson(self.val))
+    bigserialized = "8748703924870439218876954320948372058"
+    bigvalue = NumberValue(Decimal(bigserialized))
+    jackson = ObjectMapper()
+
+    def getGeneralTestData(self)->List[List[NumberValue]]:
+        return [ [self.value, self.value1, self.value1c], [self.valueb]]
+    
+    def  getGeneralTestStrings(self)->List[str]:
+        return ["12.31", "12.310000000000000001"]
+
+    def testSerialize(self) :
+        self.assertEquals(json.loads(self.serialized), self.jackson.toJson(self.value))
+
+    def testDeserialize(self):
+        self.assertEqual(self.value, self.jackson.parse(json.loads(self.serialized), NumberValue))
+
+    def  testDeserializeFromValue(self):
+        self.assertEqual(self.value, self.jackson.parse(json.loads(self.serialized), Value))
+
+    def testBigSerialize(self):
+        self.assertEqual(json.loads(self.bigserialized), self.jackson.toJson(self.bigvalue))
+
+    def testBigDeserialize(self):
+        self.assertEquals(self.bigvalue,
+                self.jackson.parse(json.loads(self.bigserialized), NumberValue))
+
+    def testSerializeShorts(self):
+        print(self.jackson.toJson([ 1, 2, 3, 4, 5 ]))
+
+    def testDeserializeShorts(self) :
+        list = self.jackson.parse(json.loads("[1,2,3,4,5]"),List[Any])
+        print(list)
+        print(list[0].__class__)
+
+    def testDeserializeMix(self):
+        list = self.jackson.parse(json.loads('["short",1,2,3,4,5]'), List[Any])
+        print(list)
+        print(list[0].__class__)
+
+    def testDeserializePlainNumber(self):
+        '''
+        Showing that we CAN deserialize big numbers without double quotes
+        correctly, if we tell jackson upfront that it's a BigDecimal.
+        '''
+        valstr = "483958743698732691487326987569213874694328974329874328947320984372498327493827432987231874681273648127";
+        val = self.jackson.parse(valstr, Decimal)
+        self.assertEqual(valstr, str(val))
+
+
+    def testNumberBlab(self):
+        # jackson.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
+        self.assertEqual(self.value, self.jackson.parse(json.loads("12.31"), Value))
         
-    def testDeserialize(self):
-        self.assertEqual(self.val, self.pyson.parse(self.valjson, Value))
-        
-    def testSerialize2(self):
-        print(str(self.pyson.toJson(NumberValue(Decimal("900")))))
-        
-    def testNonDecimal(self):
-        self.assertRaises(ValueError, lambda:NumberValue(1200))
+    def testNonNumber(self):
+        self.assertRaises(ValueError, lambda: NumberValue("abc"))
 
-    def testHash(self):
-        self.assertEqual(self.val,self.val)
-        self.assertNotEqual(self.val,self.val2)
-        self.assertNotEqual(hash(self.val), hash(self.val2))
-        
-    def testRepr(self):
-        self.assertEqual('2', repr(self.val2))
+    def testDeserializeScientificNotation(self):
+        valstr = "9E+2";
+        val = self.jackson.parse(json.loads(valstr), Value)
+        # self.assertEqual(valstr, str(val)) FIXME? 
+        self.assertEqual(val, NumberValue(Decimal("900")))
+    
Index: geniuswebcore/test/geniusweb/issuevalue/ValueDeserializerTest.py
===================================================================
--- geniuswebcore/test/geniusweb/issuevalue/ValueDeserializerTest.py	(revision 67)
+++ geniuswebcore/test/geniusweb/issuevalue/ValueDeserializerTest.py	(revision 67)
@@ -0,0 +1,16 @@
+import unittest
+from decimal import Decimal
+
+from geniusweb.issuevalue.ValueDeserializer import ValueDeserializer
+from geniusweb.issuevalue.DiscreteValue import DiscreteValue
+from geniusweb.issuevalue.NumberValue import NumberValue
+
+class ValueDeserializerTest(unittest.TestCase):
+       
+    val = ValueDeserializer()
+    
+    def testValueDeserializer(self):
+        #self.assertEqual(DiscreteValue(123),self.val.deserialize(123,1))
+        self.assertEqual(DiscreteValue("123"),self.val.deserialize("123",1))
+        self.assertEqual(NumberValue(Decimal(123)), self.val.deserialize(123,1))
+        self.assertRaises(ValueError,lambda: self.val.deserialize([1,2,3],1))
